functions/Get-HelpContent.ps1

function Get-HelpContent {
<#
.SYNOPSIS
Liest die Comment-Based Help einer Funktion aus einer `.ps1`-Datei aus und gibt die Informationen strukturiert zurück.
 
.DESCRIPTION
Die Funktion `Get-HelpContent` analysiert eine angegebene PowerShell-Datei (`.ps1`) und extrahiert den Comment-Based Help-Block einer bestimmten Funktion. Dabei werden sowohl Blockkommentare als auch Zeilenkommentare vor oder nach der Funktionsdefinition berücksichtigt.
 
Rückgegeben wird ein Objekt mit den Feldern SYNOPSIS, DESCRIPTION, PARAMETER (als Hashtable), EXAMPLE, NOTES und LINK.
 
.PARAMETER FilePath
Pfad zur Quelldatei (`.ps1`), die die Ziel-Funktion enthält.
 
.PARAMETER FunctionName
Name der Funktion, deren Hilfeinformationen extrahiert werden sollen.
 
.EXAMPLE
Get-HelpContent -FilePath '.\MyFunctions.ps1' -FunctionName 'Get-Weather'
 
Liest die Hilfeinformationen der Funktion `Get-Weather` aus der Datei `MyFunctions.ps1` aus und gibt ein strukturiertes Objekt zurück.
 
.OUTPUTS
[pscustomobject]
Ein Objekt mit folgenden Eigenschaften:
- SYNOPSIS [string]
- DESCRIPTION [string]
- PARAMETER [hashtable]
- EXAMPLE [string]
- NOTES [string]
- LINK [string]
 
.NOTES
Diese Funktion ist besonders nützlich für Skripte, die automatische Dokumentation erstellen oder die Qualität von Kommentaren analysieren. Bei fehlender Hilfe werden leere Felder zurückgegeben.
 
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$FilePath,
        [Parameter(Mandatory)][string]$FunctionName
    )

    function Find-LineIndex {
        param (
            [string[]]$Lines,
            [string]$Pattern
        )
        for ($i = 0; $i -lt $Lines.Count; $i++) {
            if ($Lines[$i] -match $Pattern) {
                return $i
            }
        }
        return -1
    }

    function Find-LineCommentsBlock {
        param(
            [string[]]$Lines,
            [int]$StartIndex,
            [int]$Direction
        )

        $commentLines = @()
        $i = $StartIndex

        while ($i -ge 0 -and $i -lt $Lines.Count) {
            $line = $Lines[$i].Trim()
            if ($line -like '#*') {
                $commentLines += $line.TrimStart('#').TrimEnd()
                $i += $Direction
            } elseif ($commentLines.Count -gt 0) {
                break
            } else {
                $i += $Direction
            }
        }
        if ($Direction -eq -1) {
            [array]::Reverse($commentLines)
        }
        return $commentLines
    }

    $lines = Get-Content -Path $FilePath
    $funcLineIndex = Find-LineIndex -Lines $lines -Pattern "function\s+$FunctionName\b"
    if ($funcLineIndex -lt 0) {
        Write-Warning "Funktion '$FunctionName' nicht gefunden."
        return $null
    }

    # Suche Block-Kommentar vor Funktion (max 10 Zeilen)
    $helpBlock = $null
    for ($offset = 1; $offset -le 10; $offset++) {
        $tryIndex = $funcLineIndex - $offset
        if ($tryIndex -lt 0) { break }
        if ($lines[$tryIndex].Trim() -like '<#*') {
            for ($endIndex = $tryIndex; $endIndex -lt $lines.Count; $endIndex++) {
                if ($lines[$endIndex].Trim() -like '*#>') {
                    $helpBlock = $lines[$tryIndex..$endIndex]
                    break
                }
            }
            if ($helpBlock) { break }
        }
    }

    # Falls kein Block-Kommentar, versuche mehrzeilige # Kommentare vor Funktion
    if (-not $helpBlock) {
        $helpBlock = Find-LineCommentsBlock -Lines $lines -StartIndex ($funcLineIndex - 1) -Direction -1
        if ($helpBlock.Count -eq 0) {
            $helpBlock = $null
        }
    }

    # Falls vor Funktion nichts, suche Block-Kommentar nach Funktion
    if (-not $helpBlock) {
        for ($offset = 1; $offset -le 10; $offset++) {
            $tryIndex = $funcLineIndex + $offset
            if ($tryIndex -ge $lines.Count) { break }
            if ($lines[$tryIndex].Trim() -like '<#*') {
                for ($endIndex = $tryIndex; $endIndex -lt $lines.Count; $endIndex++) {
                    if ($lines[$endIndex].Trim() -like '*#>') {
                        $helpBlock = $lines[$tryIndex..$endIndex]
                        break
                    }
                }
                if ($helpBlock) { break }
            }
        }
        if (-not $helpBlock) {
            $helpBlock = Find-LineCommentsBlock -Lines $lines -StartIndex ($funcLineIndex + 1) -Direction 1
            if ($helpBlock.Count -eq 0) {
                $helpBlock = $null
            }
        }
    }

    if (-not $helpBlock) {
        Write-Verbose "Keine Comment-Based Help für Funktion '$FunctionName' gefunden."
        return [pscustomobject]@{
            SYNOPSIS    = ''
            DESCRIPTION = ''
            PARAMETER   = @{}
            EXAMPLE     = ''
            NOTES       = ''
            LINK        = ''
        }
    }

    # Entferne führende <# und abschließende #> falls vorhanden
    if ($helpBlock[0] -match '^\s*<#') {
        $helpBlock[0] = $helpBlock[0] -replace '^\s*<#', ''
    }
    $lastIndex = $helpBlock.Count - 1
    if ($helpBlock[$lastIndex] -match '#>\s*$') {
        $helpBlock[$lastIndex] = $helpBlock[$lastIndex] -replace '#>\s*$', ''
    }

    $helpLines = $helpBlock | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }

    $result = @{
        SYNOPSIS    = ''
        DESCRIPTION = ''
        PARAMETER   = @{}
        EXAMPLE     = @()
        NOTES       = ''
        LINK        = ''
    }

    $currentTag = ''
    $currentParam = ''
    $buffer = @()

    function Save-Buffer {
        param (
            [string]$Tag,
            [string]$ParamName,
            [string[]]$Buffer
        )
        $text = ($Buffer -join "`n").Trim()
        switch ($Tag.ToUpper()) {
            'SYNOPSIS'    { $result.SYNOPSIS = $text }
            'DESCRIPTION' { $result.DESCRIPTION = $text }
            'PARAMETER'   { 
                if ($ParamName) {
                    if ($result.PARAMETER.ContainsKey($ParamName)) {
                        $result.PARAMETER[$ParamName] += "`n" + $text
                    } else {
                        $result.PARAMETER[$ParamName] = $text
                    }
                }
            }
            'EXAMPLE'     { $result.EXAMPLE += $text }
            'NOTES'       { $result.NOTES = $text }
            'LINK'        { $result.LINK = $text }
        }
    }

    foreach ($line in $helpLines) {
        if ($line -match '^\.(SYNOPSIS|DESCRIPTION|PARAMETER|EXAMPLE|NOTES|LINK)\s*(.*)') {
            # Speichere vorherigen Buffer
            if ($buffer.Count -gt 0) {
                Save-Buffer -Tag $currentTag -ParamName $currentParam -Buffer $buffer
                $buffer = @()
            }

            $currentTag = $matches[1].ToUpper()
            $paramText = $matches[2].Trim()

            if ($currentTag -eq 'PARAMETER') {
                # Vorherigen Parameter speichern falls offen
                if ($currentParam -ne '') {
                    Save-Buffer -Tag 'PARAMETER' -ParamName $currentParam -Buffer $buffer
                    $buffer = @()
                }
                # Neuer Parametername
                $currentParam = $paramText.Split()[0]
                # Beschreibung evtl. auf derselben Zeile?
                $desc = $paramText.Substring($currentParam.Length).Trim()
                if ($desc) { $buffer += $desc }
            }
            else {
                $currentParam = ''
                if ($paramText) {
                    $buffer += $paramText
                }
            }
        }
        elseif ($line -match '^\.\S+') {
            # Anderes unbekanntes Tag -> vorherigen Buffer speichern
            if ($buffer.Count -gt 0) {
                Save-Buffer -Tag $currentTag -ParamName $currentParam -Buffer $buffer
                $buffer = @()
            }
            $currentTag = ''
            $currentParam = ''
        }
        else {
            if ($currentTag) {
                $buffer += $line
            }
        }
    }
    # letzten Buffer speichern
    if ($buffer.Count -gt 0) {
        Save-Buffer -Tag $currentTag -ParamName $currentParam -Buffer $buffer
    }

    # EXAMPLE als Mehrzeiler zusammenführen mit doppeltem Zeilenumbruch
    $result.EXAMPLE = ($result.EXAMPLE -join "`n`n").Trim()

    return [pscustomobject]$result
}