Convert-ArgumentCompleterRegistrationScript.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

param(
    [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory, Position = 0)]
    [Alias("PSPath")]
    [string]$Path,
    
    [string]$OutputPath
)

process
{
$Path = (Resolve-Path $Path).ProviderPath
if ($null -eq $PSBoundParameters['OutputPath'])
{
    $OutputPath = ((Split-Path -Parent $Path) + "\\Transformed_" + (Split-Path -Leaf $Path))
}
$ast = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$null, [ref]$null)

$copyFrom = 0
$fullText = $ast.Extent.Text
$sb = New-Object System.Text.StringBuilder $fullText.Length

$registrations = @()

foreach ($fnDefn in $ast.FindAll({$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]}, $false))
{
    if (!$fnDefn.Body.ParamBlock) { continue }
    $attrs = $fnDefn.Body.ParamBlock.Attributes

    foreach ($attr in $attrs)
    {
        if ($attr.TypeName.Name -eq 'ArgumentCompleter')
        {
            $copyTo = $attr.Extent.StartOffset
            # Assume the attribute is indented, but we don't want that indentation
            while ($fullText[$copyTo - 1] -eq ' ' -or $fullText[$copyTo - 1] -eq "`t")
            {
                $copyTo--
            }
            $null = $sb.Append($fullText.Substring($copyFrom, $copyTo - $copyFrom))
            $copyFrom = $attr.Extent.EndOffset

            # We also want to skip the typical blank line after the attribute,
            # so first skip trailing whitespace.
            while ($fullText[$copyFrom] -eq ' ' -or $fullText[$copyFrom] -eq "`t")
            {
                $copyFrom++
            }
            # Then skip newline(s)
            while ($fullText[$copyFrom] -eq "`n" -or $fullText[$copyFrom] -eq "`r")
            {
                $copyFrom++
            }

            $registrations += [pscustomobject]@{
                    FunctionName = $fnDefn.Name
                    Attribute = $attr
            }
        }
    }

    $null = $sb.Append($fullText.Substring($copyFrom, $fnDefn.Extent.EndOffset - $copyFrom))
    $copyFrom = $fnDefn.Extent.EndOffset
}

foreach ($registration in $registrations)
{
    $functionName = $registration.FunctionName
    $isNative = $false
    $description = $null
    $parameter = $null
    $command = $null
    foreach ($namedArgument in $registration.Attribute.NamedArguments)
    {
        switch ($namedArgument.ArgumentName)
        {
        'Native'      { $isNative = $true; break }
        'Description' { $description = $namedArgument.Argument.Extent.Text; break }
        'Parameter'   { $parameter = $namedArgument.Argument.Extent.Text; break }
        'Command'
            {
            $argument = $namedArgument.Argument
            if ($argument -is [System.Management.Automation.Language.ScriptBlockExpressionAst])
            {
                $command = $argument.Extent.Text
                $command = '(' + $command.Substring(1, $command.Length - 2) + ')'
            }
            else
            {
                $command = $argument.Extent.Text
            }
            break
            }
        }
    }

$null = $sb.Append(@"



Register-ArgumentCompleter ``
    -Command $command ``
    $(if ($isNative) { "-Native" } else { "-Parameter $parameter" }) ``
    -Description $description ``
    -ScriptBlock `$function:$functionName
"@
)
    }

$sb.ToString() | Out-File -Encoding utf8 $OutputPath
}