Public/ConvertTo-MarkdownHelp.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using namespace Microsoft.PowerShell.EditorServices.Extensions
using namespace System.Management.Automation.Language

function ConvertTo-MarkdownHelp {
    <#
    .EXTERNALHELP EditorServicesCommandSuite-help.xml
    #>

    [EditorCommand(DisplayName='Generate Markdown from Closest Function')]
    [CmdletBinding()]
    param(
        [System.Management.Automation.Language.Ast]
        $Ast
    )
    end {
        $Ast = GetAncestorOrThrow $Ast -AstTypeName FunctionDefinitionAst -ErrorContext $PSCmdlet

        $settings = GetSettings
        $manifest = GetInferredManifest
        $docsPath = Join-Path (ResolveRelativePath $settings.MarkdownDocsPath) $PSCulture
        # If project uri is defined in the manifest then take a guess at what the online uri
        # should be.
        if ($projectUri = $manifest.PrivateData.PSData.ProjectUri) {
            $normalizedDocs = $PSCmdlet.SessionState.Path.NormalizeRelativePath(
                $docsPath,
                $psEditor.Workspace.Path)

            $onlineUri = $projectUri,
                         'blob/master',
                         $normalizedDocs,
                         ($Ast.Name + '.md') -join '/' -replace '\\', '/'
        }

        # Wrap this whole thing in a try/finally so we can dispose of temp files and PowerShell
        # session in event of an error or CTRL + C
        try {
            $tempFolder = Join-Path $env:TEMP -ChildPath (New-Guid).Guid
            $null = New-Item $tempFolder -ItemType Directory

            # Load the the module and create markdown in a new runspace so we don't pollute the
            # current session.
            $ps = [powershell]::Create('NewRunspace')
            $null = $ps.AddScript('
                param($manifestPath, $commandName, $onlineUrl, $tempFolder)
 
                Import-Module $manifestPath
                New-MarkdownHelp -Command $commandName `
                                 -OnlineVersionUrl $onlineUrl `
                                 -OutputFolder $tempFolder
            '
).
                AddArgument((ResolveRelativePath $settings.SourceManifestPath)).
                AddArgument($Ast.Name).
                AddArgument($onlineUri).
                AddArgument($tempFolder).
                Invoke()

            $markdownFile    = Get-ChildItem $tempFolder\*.md | Select-Object -First 1
            $markdownContent = Get-Content $markdownFile.FullName -Raw

        } finally {
            if ($ps) { $ps.Dispose() }

            if ($tempFolder -and (Test-Path $tempFolder) -and $tempFolder -match 'Temp\\^[A-z0-9-]+$') {
                Remove-Item $tempFolder -Recurse -Force
            }
        }

        if ([string]::IsNullOrWhiteSpace($markdownContent)) {
            ThrowError -Exception ([InvalidOperationException]::new($Strings.FailureGettingMarkdown)) `
                       -Id        FailureGettingMarkdown `
                       -Category  InvalidOperation `
                       -Target    $markdownContent `
                       -Show
        }

        $helpToken = $ast | Get-Token |
            Where-Object Kind -EQ Comment |
            Where-Object Text -Match '\.EXTERNALHELP|\.SYNOPSIS'

        $helpIndentLevel = $helpToken.Extent.StartColumnNumber - 1

        $newHelpComment = '<#',
                          ('.EXTERNALHELP {0}-help.xml' -f $manifest.Name),
                          '#>' -join ([Environment]::NewLine + (' ' * $helpIndentLevel))

        if ($helpToken.Text -ne $newHelpComment) {
            $helpToken | Set-ScriptExtent -Text $newHelpComment
        }

        Start-Sleep -Milliseconds 50

        if (-not (Test-Path $docsPath)) {
            $null = New-Item $docsPath -ItemType Directory
        }

        $targetMarkdownPath = '{0}\{1}.md' -f $docsPath, $Ast.Name
        if (-not (Test-Path $targetMarkdownPath)) {
            $null = New-Item $targetMarkdownPath -ItemType File
        }

        SetEditorLocation $targetMarkdownPath

        # Shape markdown according to linting rules.
        $markdownContent = $markdownContent -replace
            # Add a new line after headers.
            '([#]+) ([ \w\(\)\-_]+)(\r?\n)(?=[\w{`])', '$1 $2$3$3' -replace
            # Add powershell to code start markers.
            '(?<=\r?\n\r?\n)```(?!powershell|yaml)', '```powershell' -replace
            # Remove the trailing spaces from blank Aliases.
            '(?<=Aliases:) (?!\w)' -replace
            # Replace inconsistent example titles.
            '### Example (\d+)', '### -------------------------- EXAMPLE $1 --------------------------'


        WaitUntil { $psEditor.GetEditorContext().CurrentFile.Path -eq $targetMarkdownPath }

        Find-Ast -IncludeStartingAst -First | Set-ScriptExtent -Text $markdownContent
    }
}