P4Prompt.ps1

<#
.SYNOPSIS
  perforce prompt for windows powershell
.DESCRIPTION
  posh-p4 Module:
  Bring P4 pending changelist info to the prompt. Kinda like we have with posh-git
.NOTES
  Author: Frederic ROUSSEAU
#>


# you can override this settings from your $PROFILE
$global:P4PromptSettings = New-Object PSObject -Property @{
  BeforeDepotText= ' p4:('
  AfterDepotText = ') '
  DepotBraceColor = [System.ConsoleColor]::Gray
  DepotColor = [System.ConsoleColor]::DarkGreen
  DepotBehindColor = [System.ConsoleColor]::DarkRed
  DepotErrorColor = [System.ConsoleColor]::Cyan
  BeforeChangesText = '['
  ChangesText = "+{0} ~{1} -{2}"
  AfterChangesText = ']'
  ChangesBraceColor = [System.ConsoleColor]::Yellow
  ChangesTextColor = [System.ConsoleColor]::DarkRed
}

#invoke-expression .. with timeout
#also redirect stderr to stdout. default timeout is 2 seconds
function iext($cmd, $timeout = 2) {
  $StartTime = Get-Date
  $newPowerShell = [PowerShell]::Create().AddScript("set-location $PWD; $cmd 2>&1")
  $job = $newPowerShell.BeginInvoke()
  While (-Not $job.IsCompleted) {
    $elapsedTime = $(Get-Date) - $StartTime
    if ($elapsedTime.seconds -ge $timeout) {
      return ""
    }
  }
  $result = $newPowerShell.EndInvoke($job)
  $newPowerShell.Dispose()
  return $result
}

#get depot location and ask for password if not logged
function getDepotLocation() {
  $mapInfo = iext "p4 where ..."
  
  #display nothing if timeout
  if ($mapInfo -eq "") {
    return ""
  }
  
  #ask for password if P4PASSWD invalid or session has expired
  if (($mapInfo -like "*password (P4PASSWD) invalid*") -or ($mapInfo -like "*session has expired*")) {
    $p4pwd = Read-Host -assecurestring "p4 password"
    $p4password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($p4pwd))
    $res = ($p4password | p4 login 2>&1)
    if ($res -like "*invalid*") {
      Write-Host $res
      return
    } else {
     return getDepotLocation
    }
  } elseif ($mapInfo -like "*is not under client`'s root*") {
    return ""
  } elseif ($mapInfo -like "*unknown - use 'client' command to create it.*") {
    return ""
  } elseif ($mapInfo -NotLike "//*" -or (($mapInfo | measure-object -line).lines -gt 1)) {
    $depotLocation = "!"
  } elseif ($mapInfo.indexOf("/...") -ne -1) {
    $depotLocation = $mapInfo | % { $_.substring(0,$_.indexOf("/...")) }
  } else {
    $depotLocation = ""
  }
  return $depotLocation
}

# get a status on default changelist like "+1 ~3 -0". if request timeout return "?"
function pendingStatus() {
  $s = $global:P4PromptSettings
  $changed = 0
  $added = 0
  $deleted = 0
  $promptStatus = $s.ChangesText -f $added,$changed,$deleted
  
  $p4opened = iext "p4 opened"
  if ($p4opened -ne "") {
    foreach ($opened in $p4opened) {
      if ($opened -match "edit default change") {
        $changed += 1
      } elseif ($opened -match "add default change") {
        $added +=1
      } elseif ($opened -match "delete default change") {
        $deleted +=1
      }
    }
    $promptStatus = $s.ChangesText -f $added,$changed,$deleted
  } else {
    $promptStatus = "?"
  }
  return $promptStatus
}

#check is there are new submitted changes on depot
function changesOnDepot() {
  $p4cstat = iext "p4 cstat ..."
  if ($p4cstat -ne "") {
    $hasLastRevision = $p4cstat | select -Last 2 | select -First 1 | % { $_ -like "*have*" }
  } else {
    $hasLastRevision = "?"
  }
  return $hasLastRevision
}

#write perforce status information for current folder
#ie: p4:(//mydepot/myproj/subfolder) [+0 ~1 -0]
function Write-P4Prompt() {
  $s = $global:P4PromptSettings
  
  $depotLocation = getDepotLocation
  #do not display prompt when there is no depot
  if ($depotLocation -eq "") {
    return
  }

  if ($depotLocation -ne "!") {
    #changed files:
    $promptStatus = pendingStatus
  
    #is there any changes on depot
    $hasLastRevision = changesOnDepot
  }

  Write-Host $s.BeforeDepotText -NoNewLine -ForegroundColor $s.DepotBraceColor
  
  if ($hasLastRevision -ne "?") {
    $colorDepot = if ($hasLastRevision -eq "True") {$s.DepotColor} else {$s.DepotBehindColor}
  } else {
    $colorDepot = $s.DepotErrorColor
  }
  Write-Host $depotLocation -NoNewLine -ForegroundColor $colorDepot

  Write-Host $s.AfterDepotText -NoNewLine -ForegroundColor $s.DepotBraceColor
  
  $emptyChangesText = $s.ChangesText -f 0,0,0  
  #display changesText if there are changes..
  if ($promptStatus -ne $emptyChangesText) {
    Write-Host $s.BeforeChangesText -NoNewLine -ForegroundColor $s.ChangesBraceColor
    Write-Host $promptStatus -NoNewLine -ForegroundColor $s.ChangesTextColor
    Write-Host $s.AfterChangesText -NoNewLine -ForegroundColor $s.ChangesBraceColor
  }
}