Skip to content

Commit f4543c5

Browse files
BernieWhitevors
authored andcommitted
Update to maml rendered to pad example titles issue #119 (#322)
* Updated maml generation to pad example header #119 * Updated change log * Fix FoolLoop tests
1 parent 16a5f68 commit f4543c5

File tree

9 files changed

+167
-15
lines changed

9 files changed

+167
-15
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55

66
* Clean up trailing whitespace during markdown generation [#225](https://github.com/PowerShell/platyPS/issues/225)
77
* Preserve line breaks after headers when Update-MarkdownHelp is used [#319](https://github.com/PowerShell/platyPS/issues/319)
8+
* MAML generation now pads example titles with dash (-) to improve readability [#119](https://github.com/PowerShell/platyPS/issues/119)
89

910
## 0.8.3
1011

src/Markdown.MAML/Renderer/MamlRenderer.cs

+28-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public class MamlRenderer
1919
private static XNamespace devNS = XNamespace.Get("http://schemas.microsoft.com/maml/dev/2004/10");
2020
private static XNamespace msHelpNS = XNamespace.Get("http://msdn.microsoft.com/mshelp");
2121

22+
private static char examplePadChar = '-';
23+
private static char space = ' ';
24+
2225
/// <summary>
2326
///
2427
/// </summary>
@@ -161,7 +164,7 @@ private static XElement CreateOutput(MamlInputOutput output)
161164
private static XElement CreateExample(MamlExample example)
162165
{
163166
return new XElement(commandNS + "example",
164-
new XElement(mamlNS + "title", example.Title),
167+
new XElement(mamlNS + "title", PadExampleTitle(example.Title)),
165168
new XElement(devNS + "code", example.Code),
166169
new XElement(devNS + "remarks", GenerateParagraphs(example.Remarks)));
167170
}
@@ -189,6 +192,30 @@ private static XElement CreateLink(MamlLink link)
189192
new XElement(mamlNS + "uri", uriValue));
190193
}
191194

195+
/// <summary>
196+
/// Generate (-) padding for example title
197+
/// </summary>
198+
/// <param name="title">The title to pa.</param>
199+
/// <returns>The title padded by dashes</returns>
200+
private static string PadExampleTitle(string title)
201+
{
202+
// Filter out edge cases where title is too long or empty
203+
if (string.IsNullOrWhiteSpace(title) || title.Length >= 62)
204+
{
205+
return title;
206+
}
207+
208+
// Pad example title with dash (-) to increase readability up to 64 characters
209+
210+
int padLength = (64 - title.Length - 2) / 2;
211+
212+
return title
213+
.PadLeft(title.Length + 1, space)
214+
.PadRight(title.Length + 2, space)
215+
.PadLeft(title.Length + 2 + padLength, examplePadChar)
216+
.PadRight(title.Length + 2 + 2 * padLength, examplePadChar);
217+
}
218+
192219
private static string ConvertPSTypeToMamlType(MamlParameter parameter)
193220
{
194221
if (parameter.Type == null)

src/Markdown.MAML/Renderer/Markdownv2Renderer.cs

+13-3
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,6 @@ private string JoinWithComma(IEnumerable<string> args)
269269

270270
private bool ShouldBreak(SectionFormatOption formatOption)
271271
{
272-
// If the line break flag is set return true.
273272
return formatOption.HasFlag(SectionFormatOption.LineBreakAfterHeader);
274273
}
275274

@@ -345,7 +344,7 @@ private void AddExamples(MamlCommand command)
345344
{
346345
var extraNewLine = ShouldBreak(example.FormatOption);
347346

348-
AddHeader(ModelTransformerBase.EXAMPLE_HEADING_LEVEL, example.Title, extraNewLine: extraNewLine);
347+
AddHeader(ModelTransformerBase.EXAMPLE_HEADING_LEVEL, GetExampleTitle(example.Title), extraNewLine: extraNewLine);
349348

350349
if (!string.IsNullOrEmpty(example.Introduction))
351350
{
@@ -364,12 +363,23 @@ private void AddExamples(MamlCommand command)
364363
}
365364
}
366365

366+
private static string GetExampleTitle(string title)
367+
{
368+
var match = Regex.Match(title, @"^(-| ){0,}(?<title>([^\f\n\r\t\v\x85\p{Z}-][^\f\n\r\t\v\x85]+[^\f\n\r\t\v\x85\p{Z}-]))(-| ){0,}$");
369+
370+
if (match.Success)
371+
{
372+
return match.Groups["title"].Value;
373+
}
374+
375+
return title;
376+
}
377+
367378
public static string GetSyntaxString(MamlCommand command, MamlSyntax syntax)
368379
{
369380
return GetSyntaxString(command, syntax, DEFAULT_SYNTAX_WIDTH);
370381
}
371382

372-
373383
public static string GetSyntaxString(MamlCommand command, MamlSyntax syntax, int maxSyntaxWidth)
374384
{
375385
// TODO: we may want to add ParameterValueGroup info here,

src/Markdown.MAML/Transformer/MamlModelMerger.cs

-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ private void MergeParameters(MamlCommand result, MamlCommand metadataModel, Maml
214214
_cmdletUpdated = true;
215215
}
216216

217-
// Update the parameter with the merged in FormatOption
218217
param.FormatOption = strParam.FormatOption;
219218
}
220219

test/Markdown.MAML.Test/EndToEnd/EndToEndTests.cs

+24-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,28 @@ namespace Markdown.MAML.Test.EndToEnd
1212
public class EndToEndTests
1313
{
1414
[Fact]
15-
public void ProduceNameAndSynopsis()
15+
public void ProduceMamlFromMarkdown()
1616
{
1717
string maml = MarkdownStringToMamlString(@"
1818
# Get-Foo
1919
## Synopsis
2020
This is Synopsis
21+
## Examples
22+
### Example 1
23+
```
24+
PS C:\> Update-MarkdownHelp
25+
```
26+
27+
This is example 1 remark.
28+
29+
### Example 2: With a long title
30+
This is an example description.
31+
32+
```
33+
PS C:\> Update-MarkdownHelp
34+
```
35+
36+
This is example 2 remark.
2137
");
2238
string[] name = GetXmlContent(maml, "/msh:helpItems/command:command/command:details/command:name");
2339
Assert.Equal(1, name.Length);
@@ -26,6 +42,13 @@ This is Synopsis
2642
string[] synopsis = GetXmlContent(maml, "/msh:helpItems/command:command/command:details/maml:description/maml:para");
2743
Assert.Equal(1, synopsis.Length);
2844
Assert.Equal("This is Synopsis", synopsis[0]);
45+
46+
// Check that example title is reproduced with dash (-) padding
47+
string[] example = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:examples/command:example/maml:title");
48+
Assert.Equal(63, example[0].Length);
49+
Assert.Equal(64, example[1].Length);
50+
Assert.Matches($"^-+ Example 1 -+$", example[0]);
51+
Assert.Matches($"^-+ Example 2: With a long title -+$", example[1]);
2952
}
3053

3154
[Fact]
@@ -457,15 +480,13 @@ You can also use the PSSnapin property of the object that the Get-Command cmdlet
457480
Assert.Equal(5 + 3, examples.Length);
458481
}
459482

460-
461483
public static string[] GetXmlContent(string xml, string xpath)
462484
{
463485
List<string> result = new List<string>();
464486
XmlDocument xmlDoc = new XmlDocument();
465487
xmlDoc.LoadXml(xml);
466488
var nav = xmlDoc.CreateNavigator();
467489

468-
469490
XmlNamespaceManager xmlns = new XmlNamespaceManager(nav.NameTable);
470491
xmlns.AddNamespace("command", "http://schemas.microsoft.com/maml/dev/command/2004/10");
471492
xmlns.AddNamespace("maml", "http://schemas.microsoft.com/maml/2004/10");

test/Markdown.MAML.Test/Renderer/MamlRendererTests.cs

+58-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ public void RendererProduceNameAndSynopsis()
7474
{
7575
LinkName = "PowerShell made by Microsoft Hackathon",
7676
LinkUri = "www.microsoft.com"
77-
7877
}
7978
);
8079

@@ -100,7 +99,7 @@ public void RendererProduceNameAndSynopsis()
10099
Assert.Equal(1, parameter2.Length);
101100
Assert.Equal("This is the path parameter description.", parameter2[0]);
102101

103-
string[] example1 = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:examples/command:example[maml:title='Example 1']/dev:code");
102+
string[] example1 = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:examples/command:example[contains(maml:title,'Example 1')]/dev:code");
104103
Assert.Equal(1, example1.Length);
105104
Assert.Equal("PS:> Get-Help -YouNeedIt", example1[0]);
106105
}
@@ -167,9 +166,65 @@ public void RendererProduceEscapeXmlSpecialChars()
167166

168167
string[] synopsis = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:details/maml:description/maml:para");
169168
Assert.Equal(1, synopsis.Length);
170-
Assert.Equal(synopsis[0], command.Synopsis.Text);
169+
Assert.Equal(command.Synopsis.Text, synopsis[0]);
171170
}
172171

172+
[Fact]
173+
public void RendererProducePaddedExampleTitle()
174+
{
175+
MamlRenderer renderer = new MamlRenderer();
176+
MamlCommand command = new MamlCommand()
177+
{
178+
Name = "Get-Foo",
179+
Synopsis = new SectionBody("This is a description")
180+
};
181+
182+
var example1 = new MamlExample()
183+
{
184+
Title = "Example 1",
185+
Code = "PS:> Get-Help -YouNeedIt",
186+
Remarks = "This does stuff!"
187+
};
188+
189+
var example10 = new MamlExample()
190+
{
191+
Title = "Example 10",
192+
Code = "PS:> Get-Help -YouNeedIt",
193+
Remarks = "This does stuff!"
194+
};
195+
196+
var exampleWithTitle = new MamlExample()
197+
{
198+
Title = "Example 11: With a title",
199+
Code = "PS:> Get-Help -YouNeedIt",
200+
Remarks = "This does stuff!"
201+
};
202+
203+
var exampleWithLongTitle = new MamlExample()
204+
{
205+
Title = "Example 12: ".PadRight(66, 'A'),
206+
Code = "PS:> Get-Help -YouNeedIt",
207+
Remarks = "This does stuff!"
208+
};
209+
210+
command.Examples.Add(example1);
211+
command.Examples.Add(example10);
212+
command.Examples.Add(exampleWithTitle);
213+
command.Examples.Add(exampleWithLongTitle);
214+
215+
string maml = renderer.MamlModelToString(new[] { command });
216+
217+
// Check that example header is padded by dashes (-) unless to long
218+
string[] example = EndToEndTests.GetXmlContent(maml, "/msh:helpItems/command:command/command:examples/command:example/maml:title");
219+
Assert.Equal(4, example.Length);
220+
Assert.Equal(63, example[0].Length);
221+
Assert.Equal(64, example[1].Length);
222+
Assert.Equal(66, example[3].Length);
223+
Assert.Matches($"^-+ {example1.Title} -+$", example[0]);
224+
Assert.Matches($"^-+ {example10.Title} -+$", example[1]);
225+
Assert.Matches($"^-+ {exampleWithTitle.Title} -+$", example[2]);
226+
Assert.Matches($"^{exampleWithLongTitle.Title}$", example[3]);
227+
}
173228
}
174229

175230
}

test/Markdown.MAML.Test/Renderer/MarkdownV2RendererTests.cs

+30-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public void RendererLineBreakAfterParameter()
126126
}
127127

128128
[Fact]
129-
public void RendererLineBreakAfterExample()
129+
public void RendersExamplesFromMaml()
130130
{
131131
var renderer = new MarkdownV2Renderer(ParserMode.Full);
132132

@@ -165,10 +165,34 @@ public void RendererLineBreakAfterExample()
165165
Introduction = "Intro"
166166
};
167167

168+
var example5 = new MamlExample()
169+
{
170+
Title = "---Example 5---",
171+
Code = "PS C:\\> Get-Help -Full",
172+
Introduction = "With some dashes and no spaces"
173+
};
174+
175+
var example6 = new MamlExample()
176+
{
177+
Title = "------------------ Example 6: With extra info ------------------",
178+
Code = "PS C:\\> Get-Help -Full",
179+
Introduction = "Padded to 64 characters and spaces"
180+
};
181+
182+
var example7 = new MamlExample()
183+
{
184+
Title = "Example 7: ".PadRight(66, 'A'),
185+
Code = "PS C:\\> Get-Help -Full",
186+
Introduction = "Greater then 64 characters"
187+
};
188+
168189
command.Examples.Add(example1);
169190
command.Examples.Add(example2);
170191
command.Examples.Add(example3);
171192
command.Examples.Add(example4);
193+
command.Examples.Add(example5);
194+
command.Examples.Add(example6);
195+
command.Examples.Add(example7);
172196

173197
string markdown = renderer.MamlModelToString(command, null);
174198

@@ -179,6 +203,11 @@ public void RendererLineBreakAfterExample()
179203
// Uses line break and should be preserved
180204
Assert.Contains("### Example 3\r\n\r\n```", markdown);
181205
Assert.Contains("### Example 4\r\n\r\nIntro\r\n\r\n```", markdown);
206+
207+
// Includes title padding that should be removed
208+
Assert.Contains("### Example 5\r\n", markdown);
209+
Assert.Contains("### Example 6: With extra info\r\n", markdown);
210+
Assert.Contains($"### {example7.Title}\r\n", markdown);
182211
}
183212

184213
[Fact]

test/Pester/FullLoop.Tests.ps1

+12-2
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,19 @@ Describe 'Full loop for Add-Member cmdlet' {
7373
# this '-' before force could be screwed up
7474
0..($generatedHelpObject.examples.example.Count - 1) | ForEach-Object {
7575
It ('generate correct example ' + ($generatedHelpObject.examples.example[$_].title)) -Skip:($_ -eq 5) {
76-
($generatedHelpObject.examples.example[$_] | Out-String).TrimEnd() | Should Be ($originalHelpObject.examples.example[$_] | Out-String).TrimEnd()
76+
77+
$exampleExtractionRegex = '^(-| ){0,}(?<title>([^\f\n\r\t\v\x85\p{Z}-][^\f\n\r\t\v\x85]+[^\f\n\r\t\v\x85\p{Z}-]))(-| ){0,}(\r|\n){1,}(?<body>(\S|\s)+)';
78+
$generatedMatches = [Regex]::Match(($generatedHelpObject.examples.example[$_] | Out-String).Trim(), $exampleExtractionRegex)
79+
$originalMatches = [Regex]::Match(($originalHelpObject.examples.example[$_] | Out-String).Trim(), $exampleExtractionRegex)
80+
81+
# Confirm match completed successfully
82+
$generatedMatches.Success | Should Be $True
83+
$originalMatches.Success | Should Be $True
84+
85+
# Match clean title and clean body seperately
86+
$generatedMatches.Groups['title'].Value | Should Be $originalMatches.Groups['title'].Value
87+
$generatedMatches.Groups['body'].Value | Should Be $originalMatches.Groups['body'].Value
7788
}
78-
#($generatedHelpObject.examples | Out-String) | Should Be ($originalHelpObject.examples | Out-String)
7989
}
8090
}
8191

test/Pester/PlatyPs.Tests.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ It has mutlilines. And hyper (http://link.com).
884884
It 'has a placeholder for example' {
885885
($Help.examples.example | Measure-Object).Count | Should Be 1
886886
$e = $Help.examples.example
887-
$e.Title | Should Be 'Example 1'
887+
$e.Title | Should Match '-+ Example 1 -+'
888888
$e.Code | Should Match 'PS C:\>*'
889889
}
890890

0 commit comments

Comments
 (0)