pure-pwsh.psm1

. $PSScriptRoot/prompt.ps1
. $PSScriptRoot/async.ps1

function Log($message) {
  if ((Get-Variable pure) -and ($pure | Get-Member _logger)) { &$pure._logger $message }
  else { Write-Verbose $message }
}

filter color {$_.Split('*')[0]} # the part from '*' on is only for `$pure` display

filter ??? {param ($default) if ($_) {$_} else {$default}}

$emptyStatus = @{
  HasWorking = $false
  HasIndex   = $false
  AheadBy    = 0
  BehindBy   = 0
}

function getPromptStatus($gitStatus) {
  $status = $gitStatus |??? $emptyStatus
  return [ordered]@{
    updated  = if ($gitStatus) {Get-Date} else {[DateTime]::MinValue}
    isDirty  = ($status.HasWorking -or $status.HasIndex)
    isAhead  = ($status.AheadBy -gt 0)
    isBehind = ($status.BehindBy -gt 0)
    gitDir   = $status.GitDir
  }
}

$Script:timer = New-Object System.Timers.Timer -Property @{ Interval = 1000; AutoReset = $false }
Register-ObjectEvent $timer Elapsed -Action { [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() }

function writePromptIfChanged() {
  $newStatus = getPromptStatus (Get-GitStatus)

  if ($promptStatus -and ($newStatus)) {
    if (
      ($newStatus.isDirty -ne $promptStatus.isDirty) -or
      ($newStatus.isAhead -ne $promptStatus.isAhead) -or
      ($newStatus.isBehind -ne $promptStatus.isBehind)) {

      Log 'updating prompt from update'
      $Script:timer.Start()
    }
  }
}

function Set-PureOption() {
  [CmdletBinding()]
  param (
    [ValidateSet(
      'PwdColor',
      'BranchColor',
      'RemoteColor',
      'ErrorColor',
      'PromptColor',
      'PromptChar',
      'UpChar',
      'DownChar')]
    $Option,

    [String]
    $Value
  )

  if ($Option -like '*Color') {
    $val = if ($Value -match '^\d*m$') {$null} else {$Value}
    $Value = valueOrDefault $val $Value
  }
  $Global:pure.$option = $value
}

function valueOrDefault($value, $default) {
  "$(if ($value) {$value} else {"$([char]27)[$default"})" +
  "*$([char]27)[0m"
}

function init() {
  $psrOptions = Get-PSReadlineOption

  if ($psrOptions) {
    if ((Get-PSReadlineOption).PSObject.Properties.Name -contains 'PromptText') {
      Set-PSReadLineOption -PromptText ("{0} " -f $pure.PromptChar)
    }
    if ((Get-PSReadlineOption).PSObject.Properties.Name -contains 'ContinuationPrompt') {
      Set-PSReadLineOption -ContinuationPrompt ("{0}{0} " -f $pure.PromptChar)
    }

    if ((Get-PSReadlineOption).PSObject.Properties.Name -contains 'Colors') {
      Set-PSReadLineOption -Colors @{ ContinuationPrompt = $psrOptions.EmphasisColor }
    }

    if ((Get-PSReadlineOption).PSObject.Properties.Name -contains 'ExtraPromptLineCount') {
      Set-PSReadLineOption -ExtraPromptLineCount 2
    }
  }
}

$Global:pure = [ordered]@{
  PwdColor             = valueOrDefault $psrOptions.CommentColor "32m"
  BranchColor          = valueOrDefault $psrOptions.StringColor "36m"
  RemoteColor          = valueOrDefault $psrOptions.TypeColor "37m"
  ErrorColor           = valueOrDefault $psrOptions.ErrorColor "91m"
  PromptColor          = valueOrDefault $psrOptions.EmphasisColor "96m"
  PromptChar           = '❯'
  UpChar               = '⇡'
  DownChar             = '⇣'
  SlowCommandThreshold = [timespan]::FromSeconds(5)
  FetchPeriod          = [timespan]::FromSeconds(300)
  Debounce             = [timespan]::FromSeconds(1)
}

$Script:promptStatus = getPromptStatus $emptyStatus

$Script:watcher = [IO.FileSystemWatcher]::new()
$watcher.Path = (Get-Location).Path
$watcher.IncludeSubdirectories = $true

function registerWatcherEvent($eventName) {
  Register-ObjectEvent -InputObject $watcher -EventName $eventName -Action $updateOnChange -MessageData @{
    getNewStatus         = {getPromptStatus (& {Get-GitStatus})}
    currentStatus        = {$promptStatus}
    log                  = {Log @args}
    writePromptIfChanged = {writePromptIfChanged}
    toggleWatcher        = {$watcher.EnableRaisingEvents = $args[0] }
  }
}

$null = registerWatcherEvent Changed
$null = registerWatcherEvent Deleted

init