Measure-Help.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
function Measure-Help
{
    <#
    .Synopsis
        Determines the percentage of documentation
    .Description
        Determines the percentage of documentation in a given script
    .Example
        dir -Filter *.ps1 | Measure-Help
    .EXAMPLE
        Get-Command -Module HelpOut | Measure-Help
    .Example
        Measure-Help {
            # This script has some documentation, and then a bunch of code that literally does nothing
            $null = $null # The null equivilancy
            $null * 500 # x times nothing is still nothing
            $null / 100 # Nothing out of 100
        } | Select-Object -ExpandProperty PercentageDocumented
    .LINK
        Get-Help
    #>
    
    [CmdletBinding(DefaultParameterSetName='FilePath')]
    param(
    # The path to the file
    [Parameter(Mandatory,ValueFromPipelineByPropertyName,Position=0,ParameterSetName='FilePath')]
    [Alias('Fullname')]
    [string]
    $FilePath,

    # A PowerShell script block
    [Parameter(Mandatory,ParameterSetName='ScriptBlock',ValueFromPipelineByPropertyName)]
    [ScriptBlock]
    $ScriptBlock,

    # The name of the script being measured.
    [Parameter(ParameterSetName='ScriptBlock',ValueFromPipelineByPropertyName)]
    [string]
    $Name
    )

    begin {
        $fileList = New-Object Collections.ArrayList
        $ScriptBlockList = New-Object Collections.ArrayList
        $NameList = New-Object Collections.ArrayList

        filter OutputDocRatio {
            $scriptText = $_
            $scriptToken = [Management.Automation.PSParser]::Tokenize($scriptText, [ref]$null)

            # A quick tight little loop to sum
            # the lengths of different types in one
            # pass (Group-Object would work, but would be slower)
            $commentLength= 0
            $otherLength = 0
            $blockCommentLength = 0
            $inlineCommentLength  = 0
            $blockComments   = @()
            $inlineComments  = @()
            $totalLength = 0 
            foreach ($token in $ScriptToken) {
                $totalLength+=$token.Length
                if ($token.Type -eq 'Comment') {
                    if ($token.Content.StartsWith('<#')) {
                        $blockComments+=$token
                        $blockCommentLength += $token.Length
                    } else {
                        $inlineComments+=$token
                        $inlineCommentLength += $token.Length
                    }
                    $commentLength+=$token.Length
                } else {
                    $otherLength+=$token.Length
                }
            }
        
            # The percent is easy to calculate
            $percent =$commentLength * 100 / $totalLength
            @{
                CommentLength       = $commentLength
                TokenLength         = $otherLength
                CommentPercent      = $percent
                BlockComments       = $blockComments
                BlockCommentLength  = $blockCommentLength
                InlineComments      = $inlineComments
                InlineCommentLength = $inlineCommentLength
            }
            
        }        
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'FilePath') {
            $fileList.AddRange(@($FilePath))
        } elseif ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') {
            $null = $ScriptBlockList.Add($ScriptBlock)
            $null = $NameList.Add($Name)
        }
    }

    end {
        if ($ScriptBlockList.Count) {
            $scriptBlockIndex =0 
            foreach ($sb in $ScriptBlockList) {
                [PSCustomObject]([Ordered]@{
                    PSTypeName = "Documentation.Percentage"
                    Name = $NameList[$scriptBlockIndex]
                    ScriptBlock = $sb
                } + ($sb | OutputDocRatio))
                $scriptBlockIndex++
            }
        }

        if ($fileList.Count) {
            foreach ($f in $fileList) {
                $RF = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($F)
                if (-not $rf) { continue }
                $fileItem = Get-Item -LiteralPath $RF
                $sb = $null
                $sb = try {
                    [ScriptBlock]::Create([IO.File]::ReadAllText($RF))
                } catch {
                    $null 
                }

                if ($sb) {
                    [PSCustomObject]([Ordered]@{
                        PSTypeName = "File.Documentation.Percentage"
                        Name = $fileItem.Name
                        FilePath = "$rf"
                        ScriptBlock = $sb
                    } + ($sb | OutputDocRatio))
                }
            }
        }
    }
}