Public/ConvertTo-DiffText.ps1
|
#Requires -Version 5.1 function ConvertTo-DiffText { <# .SYNOPSIS 対応付けの結果を、コンソール表示用の行に変換する。 .DESCRIPTION 各行は、行番号とマーカーの見出し(Gutter)と、本文の断片(Segments)で構成されます。 断片は行内の変更箇所で分割されており、Changed が $true の断片を別の色で表示できます。 変更行の前後 ContextLine 行だけを残し、それ以外は 1 行の省略行にまとめます。 コンソールへの出力はしません。表示の色と、省略行の文言(「N 行省略」など)は呼び出し側で指定します。 .PARAMETER Rows Get-DiffAlignment が返す行の対応付け(TextDiff.DiffRow の配列)。それ以外は受け付けません。 .PARAMETER ContextLine 変更行の前後に残す、共通行の数(0〜100)。 .OUTPUTS TextDiff.TextLine。次の項目を持つ [PSCustomObject] の配列です。 Gutter = ' 6- ' 行番号とマーカー Role = 'Removed' | 'Added' | 'Context' | 'Omitted' Segments = TextDiff.Segment([PSCustomObject]@{ Text; Changed })の配列 OmittedCount = 省略した行数。Omitted 以外の行では 0 Segments の Text を連結すると、元の行に一致します。Omitted の行だけは Segments が空です。 .EXAMPLE PS> $rows = Get-DiffAlignment -BeforeLines @('name varchar(100),') -AfterLines @('name varchar(20),') PS> foreach ($line in ConvertTo-DiffText -Rows $rows) { >> Write-Host -Object $line.Gutter -NoNewline >> foreach ($segment in $line.Segments) { >> $color = if ($segment.Changed) { 'Yellow' } else { 'Gray' } >> Write-Host -Object $segment.Text -ForegroundColor $color -NoNewline >> } >> Write-Host >> } 削除行と追加行の 2 行を表示し、変更箇所(100 と 20)だけを黄色にします。 #> [CmdletBinding()] [OutputType('TextDiff.TextLine')] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [PSTypeName('TextDiff.DiffRow')] [psobject[]]$Rows, [ValidateRange(0, 100)] [int]$ContextLine = 3 ) $output = [System.Collections.Generic.List[object]]::new() if ($Rows.Count -eq 0) { return $output.ToArray() } # --- 表示する行を判定する --- # 変更行から ContextLine 行以内の行だけを残す。1000 行のうち 5 行の変更で 995 行の文脈を表示すると、 # 差分が読みにくくなるため(git diff と同じ方式)。 # ConvertTo-DiffHtml は保存して後から読むため、行を省略しない。 $keep = New-Object -TypeName 'bool[]' -ArgumentList $Rows.Count for ($rowIndex = 0; $rowIndex -lt $Rows.Count; $rowIndex++) { if ($Rows[$rowIndex].Kind -ceq 'Same') { continue } $from = [Math]::Max(0, $rowIndex - $ContextLine) $to = [Math]::Min($Rows.Count - 1, $rowIndex + $ContextLine) for ($keepIndex = $from; $keepIndex -le $to; $keepIndex++) { $keep[$keepIndex] = $true } } # --- 行番号の桁数を統一する --- # 途中で桁数が変化すると行の位置がずれ、差分より目立つため。 $maxNo = 0 foreach ($row in $Rows) { foreach ($no in @($row.LeftNo, $row.RightNo)) { if ($null -ne $no -and $no -gt $maxNo) { $maxNo = $no } } } $noWidth = [Math]::Max(3, ([string]$maxNo).Length) $index = 0 while ($index -lt $Rows.Count) { if (-not $keep[$index]) { # 連続する非表示行を、1 行の省略行にまとめる。 $start = $index while ($index -lt $Rows.Count -and -not $keep[$index]) { $index++ } $output.Add((ConvertTo-DiffTextLine -Gutter (' ' + ('.' * $noWidth) + ' ') -Role 'Omitted' -OmittedCount ($index - $start))) continue } $row = $Rows[$index] switch -CaseSensitive ($row.Kind) { 'Same' { $output.Add((ConvertTo-DiffTextLine -Gutter (Format-DiffGutter -Number $row.RightNo -Marker ' ' -Width $noWidth) -Role 'Context' -Text $row.Right)) } 'Changed' { # 行内の変更箇所を検出し、削除行と追加行の 2 行として出力する。 $inline = Get-InlineDiff -Left $row.Left -Right $row.Right $output.Add((ConvertTo-DiffTextLine -Gutter (Format-DiffGutter -Number $row.LeftNo -Marker '-' -Width $noWidth) -Role 'Removed' -Segments $inline.Left)) $output.Add((ConvertTo-DiffTextLine -Gutter (Format-DiffGutter -Number $row.RightNo -Marker '+' -Width $noWidth) -Role 'Added' -Segments $inline.Right)) } 'Deleted' { $output.Add((ConvertTo-DiffTextLine -Gutter (Format-DiffGutter -Number $row.LeftNo -Marker '-' -Width $noWidth) -Role 'Removed' -Text $row.Left)) } 'Added' { $output.Add((ConvertTo-DiffTextLine -Gutter (Format-DiffGutter -Number $row.RightNo -Marker '+' -Width $noWidth) -Role 'Added' -Text $row.Right)) } } $index++ } return $output.ToArray() } |