283 lines
9.9 KiB
PowerShell
283 lines
9.9 KiB
PowerShell
[CmdletBinding(SupportsShouldProcess)]
|
|
param (
|
|
[Parameter(ValueFromPipeline = $true, Position = 0)]
|
|
[String]
|
|
$Path,
|
|
|
|
[Parameter()]
|
|
[string]
|
|
$Changelist,
|
|
|
|
[Parameter()]
|
|
[switch]
|
|
$PerformSlateFiltering
|
|
)
|
|
|
|
begin {
|
|
Set-StrictMode -Version 3
|
|
$FilesToFormat = @()
|
|
}
|
|
process {
|
|
if (![string]::IsNullOrEmpty($Path)) {
|
|
$FilesToFormat += $Path
|
|
}
|
|
}
|
|
end {
|
|
function ProcessZTag {
|
|
param (
|
|
[Parameter(ValueFromPipeline)]
|
|
[String]
|
|
$Line
|
|
)
|
|
process {
|
|
switch -regex ($Line) {
|
|
"^\.\.\. depotFile (.*)" { $DepotFile = $Matches[1] }
|
|
"^\.\.\. type (.*)" { $Type = $Matches[1] }
|
|
"^\.\.\. action (.*)" { $Action = $Matches[1] }
|
|
"^\s*$" {
|
|
if ($Type -like "*text*") {
|
|
Write-Verbose "Found text file $DepotFile action $Action"
|
|
[PSCustomObject]@{
|
|
DepotPath = $DepotFile
|
|
LocalPath = $null
|
|
IsAdd = $Action -like "add"
|
|
}
|
|
}
|
|
else {
|
|
Write-Verbose "Skipping non-text file $DepotFile"
|
|
}
|
|
$DepotFile = $null
|
|
$Type = $null
|
|
$Action = $null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function filterSlateCodeFromDiffOutput($diffOutput) {
|
|
Write-Verbose "Performing Slater filtering by skipping formatting on parts of the code that looks like declarative Slate code."
|
|
|
|
$fileContent = Get-Content $LocalPath
|
|
$fileContentLines = $fileContent -split "\r?\n|\r"
|
|
|
|
# Build zones.
|
|
$currentStartLineIndex = -1
|
|
$currentBracketNesting = 0
|
|
$slateBracketZones = for ($i=0; $i -lt $fileContentLines.Length; $i++)
|
|
{
|
|
$line = $fileContentLines[$i]
|
|
if ($line.Trim() -eq "[")
|
|
{
|
|
$currentBracketNesting += 1
|
|
# We don't record the start of a nested Slate bracket.
|
|
if ($currentBracketNesting -gt 0)
|
|
{
|
|
$currentStartLineIndex = $i
|
|
}
|
|
}
|
|
elseif ($line.Trim() -eq "]" -or $line.Trim() -eq "];")
|
|
{
|
|
$currentBracketNesting -= 1
|
|
if ($currentBracketNesting -eq 0)
|
|
{
|
|
$start = $currentStartLineIndex
|
|
$currentStartLineIndex = -1
|
|
@{ "startLineIndex" = [int]$start; "endLineIndex" = [int]$i };
|
|
}
|
|
}
|
|
}
|
|
|
|
# Expand zones up and down to the nearest whitespace-only lines.
|
|
foreach ($zone in $slateBracketZones)
|
|
{
|
|
$startIndex = $zone["startLineIndex"]
|
|
$endIndex = $zone["endLineIndex"]
|
|
|
|
$originalstartIndex = $startIndex
|
|
$originalEndIndex = $endIndex
|
|
|
|
# Keep moving the start of the zone up until we find a whitespace-only line or the start of the file.
|
|
while (($startIndex -gt 0) -and ($fileContentLines[$startIndex - 1].Trim() -ne ""))
|
|
{
|
|
$startIndex--
|
|
}
|
|
|
|
# Keep moving the end of the zone down until we find a whitespace-only line or the end of the file.
|
|
while (($endIndex -lt $fileContentLines.Length-1) -and ($fileContentLines[$endIndex + 1].Trim() -ne ""))
|
|
{
|
|
$endIndex++
|
|
}
|
|
|
|
if (($originalStartIndex -ne $startIndex) -or ($originalEndIndex -ne $endIndex))
|
|
{
|
|
Write-Verbose "Expanded Slate bracket zone from ($originalStartIndex, $originalEndIndex) to ($startIndex, $endIndex)"
|
|
}
|
|
|
|
$zone["startLineIndex"] = $startIndex
|
|
$zone["endLineIndex"] = $endIndex
|
|
}
|
|
|
|
$skipCurrentHunk = $false
|
|
$diffLines = $diffOutput -split "\r?\n|\r"
|
|
for ($i=0; $i -lt $diffLines.Length; $i++)
|
|
{
|
|
$diffLine = $diffLines[$i]
|
|
if ($diffLine -match "^@@.*\+(\d+)(?:,(\d+))?")
|
|
{
|
|
$skipCurrentHunk = $false
|
|
|
|
$match = $Matches
|
|
|
|
$hunkStartLine = [int]$match.1
|
|
$hunkLineCount = [int]$match.2
|
|
if (-Not $hunkLineCount)
|
|
{
|
|
$hunkLineCount = 1
|
|
}
|
|
$hunkEndLine = $hunkStartLine + $hunkLineCount - 1
|
|
|
|
$hunkStartLineIndex = $hunkStartLine - 1
|
|
$hunkEndLineIndex = $hunkEndLine - 1
|
|
|
|
foreach ($slateBracketZone in $slateBracketZones)
|
|
{
|
|
$zoneStart = $slateBracketZone["startLineIndex"]
|
|
$zoneEnd = $slateBracketZone["endLineIndex"]
|
|
|
|
$hunkStartInside = ($zoneStart -le $hunkStartLineIndex) -and ($hunkStartLineIndex -le $zoneEnd)
|
|
$hunkEndInside = ($zoneStart -le $hunkEndLineIndex) -and ($hunkEndLineIndex -le $zoneEnd)
|
|
|
|
if ($hunkStartInside -or $hunkEndInside)
|
|
{
|
|
Write-Verbose "Diff hunk overlaps Slate bracket zone, skipping the following lines:"
|
|
$skipCurrentHunk = $true
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($skipCurrentHunk)
|
|
{
|
|
Write-Verbose "SKIPPING: $diffLine"
|
|
}
|
|
else
|
|
{
|
|
Write-Output $diffLine
|
|
}
|
|
}
|
|
}
|
|
|
|
# Filter files on the command line to those actually present in perforce and convert to depot paths
|
|
if (0 -ne $FilesToFormat.Length) {
|
|
Write-Verbose "Checking perforce status of requested files $FilesToFormat"
|
|
[array]$DepotFiles = p4 "-ztag" "fstat" "-Ro" @FilesToFormat | ProcessZTag
|
|
}
|
|
else {
|
|
if (![string]::IsNullOrEmpty($Changelist)) {
|
|
Write-Verbose "Requested files for changelist $Changelist"
|
|
# Get opened files in a specific changelist
|
|
$P4Args = @("-ztag", "opened", "-c", $Changelist);
|
|
}
|
|
else {
|
|
Write-Verbose "Requested files for all pending changelists"
|
|
# Get current client name
|
|
switch -regex (p4 "-ztag" "info") {
|
|
"^\.\.\. clientName (.*)" { $ClientName = $Matches[1] }
|
|
}
|
|
if ([string]::IsNullOrEmpty($ClientName)) {
|
|
throw "Unable to get perforce client name"
|
|
}
|
|
# Get all opened files
|
|
$P4Args = @("-ztag", "opened", "-C", $ClientName);
|
|
}
|
|
|
|
Write-Verbose "Fetching open files from Perforce $P4Args"
|
|
[array]$DepotFiles = p4 @P4Args | ProcessZTag
|
|
}
|
|
|
|
if ($DepotFiles.Length -eq 0) {
|
|
throw "No files to format"
|
|
}
|
|
$Index = 0
|
|
switch -regex (p4 -ztag "where" @($DepotFiles | Select-Object -ExpandProperty "DepotPath")) {
|
|
"^\.\.\. path (.*)" {
|
|
$DepotFiles[$Index].LocalPath = $Matches[1]
|
|
++$Index
|
|
}
|
|
}
|
|
|
|
# We need this to use the function from within Foreach-Object.
|
|
$filterSlateCodeFromDiffOutputSource = ${function:filterSlateCodeFromDiffOutput}.ToString()
|
|
|
|
$Root = $PSScriptRoot
|
|
$DepotFiles | Foreach-Object -ThrottleLimit 5 -Parallel {
|
|
$VerbosePreference = $using:VerbosePreference
|
|
$WhatIfPreference = $using:WhatIfPreference
|
|
$PerformSlateFiltering = $using:PerformSlateFiltering
|
|
${function:filterSlateCodeFromDiffOutput} = $using:filterSlateCodeFromDiffOutputSource
|
|
$Root = $using:Root
|
|
$LocalPath = $_.LocalPath
|
|
$IsAdd = $_.IsAdd
|
|
|
|
if ([string]::IsNullOrEmpty($LocalPath)) {
|
|
throw "Unexpected empty path"
|
|
}
|
|
if ($false -eq (Test-Path $LocalPath)) {
|
|
Write-Verbose "Skipping non-existed (deleted/moved?) file $LocalPath"
|
|
return;
|
|
}
|
|
|
|
$clangFormatPath = Join-Path $Root "clang-format.exe"
|
|
$formatLocalPath = Join-Path $Root "experimental.clang-format"
|
|
# For newly added files, just format the entire file
|
|
if ($IsAdd) {
|
|
if ($WhatIfPreference) {
|
|
Write-Host "Skipping full format of newly added file $LocalPath"
|
|
}
|
|
else {
|
|
& $clangFormatPath "-style=file:$formatLocalPath" "-i" $LocalPath | Out-Null
|
|
}
|
|
}
|
|
else {
|
|
$clangFormatDiffPath = Join-Path $Root "clang-format-diff.py"
|
|
|
|
$diffOutput = p4 "diff" "-du0" $LocalPath
|
|
|
|
if ($PerformSlateFiltering) {
|
|
$diffOutput = filterSlateCodeFromDiffOutput $diffOutput
|
|
Write-Verbose "Final diff given to clang-format-diff:"
|
|
foreach ($line in $diffOutput) {
|
|
Write-Verbose $line
|
|
}
|
|
}
|
|
|
|
# Check output is what we expect and bail if not
|
|
if ($diffOutput.Length -lt 2) {
|
|
throw "Diff output for $LocalPath unexpectedly short"
|
|
$diffOutput | Write-Host
|
|
}
|
|
if ($diffOutput[0] -notlike "---*") {
|
|
throw "Missing old file path in diff output"
|
|
}
|
|
if ($diffOutput[1] -notlike "+++*") {
|
|
throw "Missing new file path in diff output"
|
|
}
|
|
|
|
$pythonArgs = @($clangFormatDiffPath, "-binary=$clangFormatPath", "-style=file:$formatLocalPath", "-v", "-i")
|
|
|
|
if ($WhatIfPreference) {
|
|
Write-Host "Skipping running diff script for file $LocalPath"
|
|
$diffOutput | Select-Object -first 2 | Write-Host
|
|
}
|
|
else {
|
|
$diffFormatOutput = Write-Output $diffOutput | python @pythonArgs | Out-String
|
|
$diffFormatExitCode = $LASTEXITCODE
|
|
|
|
if ($diffFormatExitCode -ne 0) {
|
|
throw "Failed to run clang-format-diff.py on $LocalPath.`nclang-format-diff.py output:`n$diffFormatOutput"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|