Extensions/Git.Grep.UGit.Extension.ps1

<#
.SYNOPSIS
    git grep extension
.DESCRIPTION
    Outputs matches from git grep.
    
    When possible, the regular expression will be converted into PowerSEhll so that the .Match populates accurately.
.EXAMPLE
    git grep '-i' example # look for all examples in the repository
.LINK
    Out-Git
#>

[Management.Automation.Cmdlet("Out","Git")]         # It extends Out-Git
[ValidatePattern("^git grep",Options='IgnoreCase')] # when the pattern is "git grep"
[OutputType('Git.Grep.Match')]
param()

begin {
    $grepLines = @()
    $multilineContext = $false
    $afterLines  = 0
    $beforeLines = 0
    $AfterContext  = $GitCommand -cmatch '\s-(?>A|-after-context)\s(?<ac>\d+)'
    if ($AfterContext) {
        $afterLines = $matches.ac -as [int]
    }
    $BeforeContext = $GitCommand -cmatch '\s-(?>B|-before-context)\s(?<bc>\d+)'
    if ($BeforeContext) {
        $beforeLines = $matches.bc -as [int]
    }

    if ($afterLines -or $beforeLines) {
        $multilineContext = $true
    }
    $CurrentMatch = $null
    $GrepLineMatchIndex = -1   
    $grepTerm = for ($gitArgIndex =1 ; $gitArgIndex -lt $gitArgument.Length; $gitArgIndex++) {
        $gitArg = $GitArgument[$gitArgIndex]
        if ($gitArg -notmatch '^-' -and 
            $gitArgument[$gitArgIndex - 1] -notmatch '^-(?>-after-context|-before-context|O|B|A)'
        ) {
            $gitArg
            break
        }
    }
 
    $caseSensitive = $true
    if ($gitArgument -eq '-i' -or $gitArgument -eq '--ignore-case') {
        $caseSensitive = $false
    }
}

process {
    $gitOutputLine = $_
    $grepLines += $_
    if ($gitOutputLine -match '^([^\:]+)\:') {
        if ($CurrentMatch) {
            $CurrentMatch.GitOutputLines = $grepLines[0..($grepLines.Length -2)]
            [PSCustomObject]$CurrentMatch
        }
        $GrepLineMatchIndex = $grepLines.Length - 1
        $CurrentMatch = [Ordered]@{
            PSTypeName = 'Git.Grep.Match'
            GitPath    = $matches.1
            GitRoot    = $gitRoot
            Pattern    = $grepTerm
        }
        $gitOutputLine = $gitOutputLine -replace '^([^\:]+)\:'
        if ($GitCommand -cmatch '--line-number') {            
            $null = $gitOutputLine -match '^(?<line>\d+)\:'
            $CurrentMatch.LineNumber = $matches.line -as [int]
            $gitOutputLine = $gitOutputLine -replace '^(?<line>\d+)\:'
        }
        
        if ($GitCommand -cmatch '--column') {
            $null = $gitOutputLine -match '^(?<column>\d+)\:'
            $CurrentMatch.ColumnNumber = $matches.column -as [int]
            $gitOutputLine = $gitOutputLine -replace '^(?<column>\d+)\:'
        }

        
        $CurrentMatch.Line = $gitOutputLine
        
        # Go back from the previous line to get lines before
        if ($beforeLines) {            
            $CurrentMatch.Before = $grepLines[
                ($GrepLineMatchIndex - 1)..
                ($GrepLineMatchIndex - $beforeLines)
            ] -replace "^$($CurrentMatch.GitPath)-(\d+)?-(\d+)?"
        }
    }

    if ($gitOutputLine -match '^--$') {
        if ($afterLines) {
            $CurrentMatch.After = $grepLines[
                ($GrepLineMatchIndex + 1)..
                ($GrepLineMatchIndex + $afterLines)
            ] -replace "^$($CurrentMatch.GitPath)-(\d+)?-(\d+)?"
        }        
        $CurrentMatch.GitOutputLines = $grepLines
        [PSCustomObject]$CurrentMatch
        $grepLines = @()
        $GrepLineMatchIndex = -1
        # Separator, reset $grepLines and finish $currentMatch
    }
    
}

end {
    if ($CurrentMatch) {
        $CurrentMatch.GitOutputLines = $grepLines
        [PSCustomObject]$CurrentMatch
    }
}