ShellServer.psm1

# if you change the port number here, you must change below too
$Port = 5432

$LocalHost = '127.0.0.1'
$Enc = [System.Text.Encoding]::UTF8
$Sock = New-Object System.Net.Sockets.UdpClient
$Sock.Connect($LocalHost, $Port)

$Address = [System.Net.IpAddress]::Parse($LocalHost)
$End = New-Object System.Net.IPEndPoint $Address, $Port

$Buffer = $Enc.GetBytes('2Initpwsh')
$Sock.Send($Buffer) > $nul

Register-EngineEvent PowerShell.Exiting -action {
  $Sock = New-Object System.Net.Sockets.UdpClient
  #
  # Change port number below if you changed above
  #
  $Sock.Connect('127.0.0.1', 5432)
  $Sock.Send([System.Text.Encoding]::UTF8.GetBytes('2Exit'))
} > $nul

$firstEnterKeyPress = 1
$lastCmdId = (Get-History -Count 1).Id

Set-PSReadLineKeyHandler -Key Enter -ScriptBlock {
  $origDollarQuestion = $global:?
  $origLastExitCode = $global:LASTEXITCODE

  $line = $cursor = $null 
  [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

  $currentCmdId = (Get-History -Count 1).Id
  if (($currentCmdId -ne $Script:lastCmdId) -or (-not $line) -or ($firstEnterKeyPress)) {
    $Script:lastCmdId = $currentCmdId

    # will treat the enter after an empty line as the first enter key press
    $Script:firstEnterKeyPress = [int](-not $line)

    [Console]::SetCursorPosition(0, [Console]::GetCursorPosition().Item2 - 2)
    Write-Host -NoNewline "`e[J`e[34m❯`e[0m $line"
  }

  [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()

  $global:LASTEXITCODE = $origLastExitCode
  if ($global:? -ne $origDollarQuestion) {
      if ($origDollarQuestion) {
          1+1
      } else {
          Write-Error '' -ErrorAction 'Ignore'
      }
  }
}

$LightThemeColors = @{
  Command                = "DarkYellow"
  Comment                = "DarkGreen"
  ContinuationPrompt     = "DarkGray"
  Default                = "DarkGray"
  Emphasis               = "DarkBlue"
  InlinePrediction       = "DarkGray"
  Keyword                = "Green"
  ListPrediction         = "DarkYellow"
  ListPredictionSelected = "`e[30;5;238m"
  Member                 = "Black"
  Number                 = "Black"
  Operator               = "DarkGray"
  Parameter              = "DarkGray"
  Selection              = "`e[30;5;238m"
  String                 = "DarkCyan"
  Type                   = "Black"
  Variable               = "Green"
}

$DefaultThemeColors = @{
  Command                = "`e[93m"
  Comment                = "`e[32m"
  ContinuationPrompt     = "`e[37m"
  Default                = "`e[37m"
  Emphasis               = "`e[96m"
  InlinePrediction       = "`e[38;5;238m"
  Keyword                = "`e[92m"
  ListPrediction         = "`e[33m"
  ListPredictionSelected = "`e[48;5;238m"
  Member                 = "`e[97m"
  Number                 = "`e[97m"
  Operator               = "`e[90m"
  Parameter              = "`e[90m"
  Selection              = "`e[30;47m"
  String                 = "`e[36m"
  Type                   = "`e[37m"
  Variable               = "`e[92m"
}

function PSThemeChange {
  if ((get-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize).SystemUsesLightTheme) {
    Set-PSReadLineOption -Colors $LightThemeColors
  } else {
    Set-PSReadLineOption -Colors $DefaultThemeColors
  }
}

PSThemeChange

Remove-Alias ls -ErrorAction 'Ignore'

function global:prompt {
  $origDollarQuestion = $global:?
  $origLastExitCode = $global:LASTEXITCODE

  if (-Not (Test-Path 'ENV:VIRTUAL_ENV')) {
    Write-Host ''
  }

  $width = $Host.UI.RawUI.BufferSize.Width
  $duration = (Get-History -Count 1).Duration
  if (-Not $duration) {
      $duration = 0.0
  } else {
      $duration = [string]$duration.TotalSeconds
    }

  $Buffer = $Enc.GetBytes('1' + [int] $origDollarQuestion + (Get-Location).Path + ";$width;$duration")
  $Sock.Send($Buffer) > $nul

  $response = $Enc.GetString($Sock.Receive([ref] $End))
  $ExecutionContext.InvokeCommand.ExpandString($response)

  $global:LASTEXITCODE = $origLastExitCode
  if ($global:? -ne $origDollarQuestion) {
      if ($origDollarQuestion) {
          1+1
      } else {
          Write-Error '' -ErrorAction 'Ignore'
      }
  }
}

function p {
  param($arg)

  if (-not $arg) {
      Set-Location
  }

  else {
    if ($args) {
      $arg += ' ' + $args -join ' '
    }

    $Buffer = $Enc.GetBytes("3$arg")
    $Sock.Send($Buffer) > $nul
    $response = $Enc.GetString($Sock.Receive([ref] $End))

      if ($response) {
       Set-Location $response
      } else {
        Write-Output "ShellServer: No match found."
        }
      }
}


function pz {
  $Buffer = $Enc.GetBytes('4')
  $Sock.Send($Buffer) > $nul
  $response = $Enc.GetString($Sock.Receive([ref] $End))
  $query = $args -Join ' '

  if ($query) {
    $answer = Write-Output $response | fzf --height=~20 --layout=reverse -q $query
  } else {
      $answer = Write-Output $response | fzf --height=~20 --layout=reverse
    }

  $Sock.Send($Enc.GetBytes('4' + $answer)) > $nul
   Set-Location $answer
}


function ls {
  param($opts, $path)

  if ($path) {

    try {$resPath = (Resolve-Path $path -ErrorAction 'Stop').Path} catch {$resPath = (Get-Error).TargetObject}
  } else {
      $resPath = (Get-Location).Path
    }

  $Buffer = $Enc.GetBytes("5$opts;$resPath")
  $Sock.Send($Buffer) > $nul
  $response = $Enc.GetString($Sock.Receive([ref] $End))
  $ExecutionContext.InvokeCommand.ExpandString($response)
}


function ll {
  ls '-cil' ($args -join ' ')
}


function la {
  ls '-acil' ($args -join ' ')
}


function Switch-Theme {
  [CmdletBinding()]
  param(
    [Parameter()]
    [ArgumentCompletions('all', 'terminal', 'system', 'blue')]
    $arg
  )

  $Buffer = $Enc.GetBytes("6$arg")
  $Sock.Send($Buffer) > $nul
  Start-Sleep .1
  if (($arg -eq 'terminal') -Or (-Not $arg)) { PSThemeChange }
}


function Search-History {
  $width = $Host.UI.RawUI.BufferSize.Width
  $height = $Host.UI.RawUI.BufferSize.Height

  $arg = $args -join ' '
  $Buffer = $Enc.GetBytes("7$arg;$width;$height")
  $Sock.Send($Buffer) > $nul

  $response = $Enc.GetString($Sock.Receive([ref] $End))
  $ExecutionContext.InvokeCommand.ExpandString($response)
}


$completions = @($Enc.GetString($Sock.Receive([ref] $End)) -split ';')
$scriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $completions | Where-Object {
        $_ -like "$wordToComplete*"
    } | ForEach-Object {
          $_
    }
}
Register-ArgumentCompleter -CommandName p -ParameterName arg -ScriptBlock $scriptBlock

Export-ModuleMember -Function @("p", "pz", "ls", "ll", "la", "Switch-Theme", "Search-History")