Skip to content

Multiline examples don't display as markdown code #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Kreloc opened this issue Jul 30, 2016 · 16 comments
Closed

Multiline examples don't display as markdown code #180

Kreloc opened this issue Jul 30, 2016 · 16 comments
Labels
Area-HelpEngineProblem Issue deals with PowerShell help system

Comments

@Kreloc
Copy link

Kreloc commented Jul 30, 2016

Steps to reproduce

Have a function with multiple lines in an .EXAMPLE section

.EXAMPLE
     $verbs = Get-Verb
     $verbs

Expected behavior

------------------------- EXAMPLE 1 -------------------------

     $verbs = Get-Verb
     $verbs

Actual behavior

------------------------- EXAMPLE 1 -------------------------

     $verbs = Get-Verb

$verbs

v0.5.0

@vors
Copy link
Collaborator

vors commented Jul 31, 2016

@Kreloc ah, interesting. Thank you for reporting it!
It's yet another help-engine problem that we probably should work-around in platyPS.

@vors vors added the Area-HelpEngineProblem Issue deals with PowerShell help system label Jul 31, 2016
@jdhitsolutions
Copy link

I might as well add something to the mix here. I often have multiple commands in an example:

PS C:\> $a = Get-Service
PS C>> $a | group Status

Followed by a blank line and then an explanation. I'd love for the PowerShell commands to be treated as a code block.

@vors
Copy link
Collaborator

vors commented Dec 4, 2017

@jdhitsolutions sorry for the super late reply, slipped out of my attention :(

If you still care, this is a duplicate requirement of #294 you can read the long conversation there for more details.
TLDR: all example code snippets should get powershell moniker.

@BernieWhite
Copy link
Contributor

So this is an issue because the PowerShell help system considers the second line a remark instead of code regardless of if it's prefixed with PS C:\>.

@midacts
Copy link

midacts commented Mar 13, 2018

It seems like this issue probably isn't being looked at anymore or isn't going to be resolved, right?
Does anyone have a work around?

I guess just making everything be on one line

@BernieWhite
Copy link
Contributor

@midacts anything not closed will be looked at but more common issues are prioritised.

@BernieWhite
Copy link
Contributor

This particular issue as i understand it only applies one inital help import into markdown. So a copy/paste from help to markdown would also allow you to work around the issue.

@Greg-Smulko
Copy link
Contributor

Greg-Smulko commented Sep 24, 2019

I created a big of regex magic to fix this issue, it can help somebody hopefully.

    New-MarkdownHelp -Module MyModuleToDocument -OutputFolder $OutputDir -Force

    $OutputDir | Get-ChildItem -File | ForEach-Object {
        # fix formatting in multiline examples
        $content = Get-Content $_.FullName -Raw
        $newContent = $content -replace '(## EXAMPLE [^`]*)(```\r\n)([^`]*)(```\r\n)(\r\n)([^#]*)(\r\n\r\n)+([^#]+)(#)', '$1$2$3$6$5$4$7$8$9'
        if ($newContent -ne $content) {
            Set-Content -Path $_.FullName -Value $newContent -Force
        }
    }

@vors
Copy link
Collaborator

vors commented Sep 24, 2019

Would you care to try to include such heuristic in platypus when we generate the examples code from Get-Help object?

@Greg-Smulko
Copy link
Contributor

@vors, TL;DR: I gave it a try and realized that there is no way to come up with the heuristics that works for all scenarios.

Unfortunately, it seems that there is no way to distinguish between these two scenarios:

.EXAMPLE
PS C:\> Test-PlatyPSFunction "Multiline example, code only"
Generated output (no new line between these two lines)

and

.EXAMPLE
Test-PlatyPSFunction "Single line of code"

Single line of description - a new line that separates the code section doesn't matter :(

As it was said before, the problem is that Get-Help treats only the first line as code, and all the rest as remarks. What's even worse, it also trims all the new lines, so the author's intent is lost.

I came up with a couple of regexes:

  1. a greedy one, always treats the first paragraph from the remarks as code
'(## EXAMPLE [^`]+?```\r\n[^`\r\n]+?\r\n)(```\r\n\r\n)([^#]+?\r\n)(\r\n)', '$1$3$2$4'
  1. not as greedy as the previous one; always treats the last paragraph as a remark. It still doesn't work correctly for a single line of code and two paragraphs of remarks. It also doesn't work if there are no remarks at all.
'(## EXAMPLE [^`]+?```\r\n[^`\r\n]+?\r\n)(```\r\n\r\n)([^#]+?\r\n)(\r\n)([^#]+)(#)', '$1$3$2$4$5$6'

Feel free to test the above e.g. on http://regexstorm.net/tester using the below test data:

test data

Generated markdown:


	"---
	external help file:
	Module Name:
	online version:
	schema: 2.0.0
	---

	# Test-PlatyPSFunction

	## SYNOPSIS
	Copies an item from one location to another.

	## SYNTAX

	```
	Test-PlatyPSFunction [-Confirm]
	```

	## DESCRIPTION
	The Copy-Item cmdlet copies an item (...)

	## EXAMPLES

	### EXAMPLE 1
	```
	Test-PlatyPSFunction "Just a single line example, without remarks"
	```

	### EXAMPLE 2
	```
	Test-PlatyPSFunction "Multiline example, code only"
	```

	PS C:\\\> Generated output (no new line between these two lines, but it doesn't really matter)

	### EXAMPLE 3
	```
	Test-PlatyPSFunction "Single line of code"
	```

	Single paragraph of remarks - a new line that separates the code section doesn't matter :(

	### EXAMPLE 4
	```
	Test-PlatyPSFunction "Single line of code"
	```

	First paragraph of remarks - a new line that separates the code section doesn't matter :(

	Second paragraph of remarks - the first paragraph will be treated as code :(

	### EXAMPLE 5
	```
	$Session = New-PSSession -ComputerName "Server01" -Credential "Contoso\PattiFul"
	```

	Copy-Item "C:\MyRemoteData\test.log" -Destination "D:\MyLocalData\" -FromSession $Session
	My output

	The first command creates a session to the remote computer named Server01 with the credential of Contoso\PattiFul and stores the results in the variable named $Session.

	The second command uses the Copy-Item cmdlet to copy test.log from the remote C:\MyRemoteData\ to the local D:\MyLocalData folder using the session information stored in the $Session variable.
	This command does not delete the original file.

	## PARAMETERS

	### -Confirm
	Prompts you for confirmation before running the cmdlet.

	```yaml
	Type: SwitchParameter
	Parameter Sets: (All)
	Aliases:

	Required: False
	Position: Named
	Default value: False
	Accept pipeline input: False
	Accept wildcard characters: False
	```

	## INPUTS

	### System.String. You can pipe a string that contains a path to this cmdlet.
	## OUTPUTS

	### None or an object representing the copied item.
	## NOTES

	## RELATED LINKS

	[Online Version: http://go.microsoft.com/fwlink/?LinkId=821574]()

	"

Original comment-based help template based on https://technet.microsoft.com/en-us/library/hh847834.aspx :

.OUTPUTS
None or an object representing the copied item.

.EXAMPLE
PS C:\> Test-PlatyPSFunction "Just a single line example, without remarks"

.EXAMPLE
PS C:\> Test-PlatyPSFunction "Multiline example, code only"
PS C:\> Generated output (no new line between these two lines, but it doesn't really matter)

.EXAMPLE
PS C:\> Test-PlatyPSFunction "Single line of code"

Single paragraph of remarks - a new line that separates the code section doesn't matter :(
    
.EXAMPLE
PS C:\> Test-PlatyPSFunction "Single line of code"

First paragraph of remarks - a new line that separates the code section doesn't matter :(

Second paragraph of remarks - the first paragraph will be treated as code :(

.EXAMPLE
$Session = New-PSSession -ComputerName "Server01" -Credential "Contoso\PattiFul"
Copy-Item "C:\MyRemoteData\test.log" -Destination "D:\MyLocalData\" -FromSession $Session
My output

The first command creates a session to the remote computer named Server01 with the credential of Contoso\PattiFul and stores the results in the variable named $Session.

The second command uses the Copy-Item cmdlet to copy test.log from the remote C:\MyRemoteData\ to the local D:\MyLocalData folder using the session information stored in the $Session variable. This command does not delete the original file.

.LINK
Online Version: http://go.microsoft.com/fwlink/?LinkId=821574

I can open a PR for the above, but I'm a bit worried about introducing a breaking change here.
Also, it doesn't support every scenario properly, so I'm pretty sure that it may break things for some people.

Another option is to add this as an optional feature, but then probably just letting people use the above regexes (or different ones) is easier, safer and more reasonable.

The heuristics can be improved by checking if the first paragraph of remarks looks more like code or more like text, but it feels like creating a lot of complexity just to create a workaround for a bug.

I assume that there is no way to have the Get-Help itself fixed?

@vors
Copy link
Collaborator

vors commented Oct 11, 2019

First, wow, TIL about this nice trick from you

test data

Foo

<details><summary>test data</summary>
<p>
Foo
</p>
</details>

That's awesome, going to use from now on.

I think for the future versions of powershell, we can definitely fix it.
The bug seems to be in this method
https://github.com/PowerShell/PowerShell/blob/bd6fdae73520931f0d27a29d6290e18761772141/src/System.Management.Automation/help/HelpCommentsParser.cs#L496

The code is some state machine, if you feel like opening at issue in the PowerShell repo and maybe even trying to solve it - that would be amazing.

@vors
Copy link
Collaborator

vors commented Oct 11, 2019

To be more precise, the bug is here:

https://github.com/PowerShell/PowerShell/blob/bd6fdae73520931f0d27a29d6290e18761772141/src/System.Management.Automation/help/HelpCommentsParser.cs#L514,L521

We are cutting off the code_str at the first \n char.
I think it's reasonable to rewrite this hand-written state machine as a regex parsing. The logic in the file is generating xmls on the fly so the performance bottleneck is not going to be in one regex application.

@bravo-kernel
Copy link

bravo-kernel commented Dec 25, 2019

@Greg-Smulko nice job, I just tested this fix in Powershell 7. Is my conclusion correct in that this will only work for adjacent lines, like shown below.

PS> line 1
   line 2
       line 3
   line 4
line 5

The five lines above will be recognized (and fenced) as code by PlatyPS and will be correctly aligned. 

Everything below the five lines will be used as the description.

It is not possible to use empty lines in the multi-line code, code lines must be adjacent.

@Greg-Smulko
Copy link
Contributor

@bravo-kernel , yes, you're correct. Everything up to the first double new line is treated as code, and then the rest is a description. The reasoning behind this is that a multi-paragraph description is quite common, and otherwise, it wouldn't be possible to figure out where the code ends and description begins (without some heuristics or relying on a prompt PS>).

If you want to take a look at supported examples, please take a look at the tests added as part of the PR in the PowerShell repo.

@bravo-kernel
Copy link

bravo-kernel commented Dec 29, 2019

@Greg-Smulko thank you kindly for confirming, always better to be 100% sure beforehand.

For other users that might be interested in this topic... the Pester documentation website required supporting more complex code examples (that contain double-newlines in the code itself). After trying various solutions, the one that won is Code Fence Detection, applied during PlatyPS post-processing.

It basically allows you to use common markdown code fences in your Get-Help examples. For more information see:

ps this still respects your PS7 native multi-line support 🎉

Vendor Agnostic

We added support for vendor agnostic output so our module can now also be used by users who want to render code fenced get-help but do not want to use Docusaurus to serve their pages.

@Kreloc
Copy link
Author

Kreloc commented Dec 31, 2019

Marking as closed as it is no longer an issue due to the excellent work done by @Greg-Smulko based on @vors comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-HelpEngineProblem Issue deals with PowerShell help system
Projects
None yet
Development

No branches or pull requests

7 participants