-
-
Save gerane/a66cfac8364c86364e7f2010bca0579d to your computer and use it in GitHub Desktop.
powershell peco
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Load | |
Split-Path $MyInvocation.MyCommand.Path -Parent | Push-Location | |
Get-ChildItem poco_*.ps1 | %{. $_} | |
Pop-Location | |
function Select-Poco | |
{ | |
Param | |
( | |
[Object[]]$Property = $null, | |
[string]$Query = '', | |
[ValidateSet('match', 'like', 'eq')] | |
[string]$Filter = 'match', | |
[switch]$CaseSensitive = $false, | |
[switch]$InvertFilter = $false, | |
[string]$Prompt = 'Query', | |
[ValidateSet('TopDown', 'BottomUp')] | |
[string]$Layout = 'TopDown', | |
[HashTable]$Keymaps = (New-PocoKeymaps) | |
) | |
$Items = $input | %{,$_} | |
$config = New-Config $Items $Property $Prompt $Layout $Keymaps # immutable | |
$state = New-State $Query $Filter $CaseSensitive $InvertFilter $config # mutable | |
Backup-ScrBuf | |
Clear-Host | |
$action = 'None' | |
while ($action -ne 'Cancel' -and $action -ne 'Finish') | |
{ | |
Write-Screen $state $config | |
$key, $keystr = Get-PocoKey | |
$action = Get-Action $config $keystr | |
$state = Update-State $state $config $action $key | |
} | |
Restore-ScrBuf | |
if ($action -eq 'Finish') {$state.Entry} | |
trap | |
{ | |
Restore-ScrBuf | |
break | |
} | |
} | |
Set-Alias poco Select-Poco | |
Export-ModuleMember -Function "*-Poco*" -Alias "poco" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function New-Config ($Items, $Property, $Prompt, $Layout, $Keymaps) | |
{ | |
@{ | |
'Input' = $Items | |
'Property' = $Property | |
'Prompt' = $Prompt | |
'Layout' = $Layout | |
'Keymaps' = $Keymaps | |
} | |
} | |
function New-State ($Query, $Filter, $CaseSensitive, $InvertFilter, $Config) | |
{ | |
$state = @{ | |
'Query' = $Query | |
'Filter' = $Filter | |
'CaseSensitive' = $CaseSensitive | |
'InvertFilter' = $InvertFilter | |
'Acrion' = 'Identity' | |
'Entry' = @() | |
'PrevLength' = 0 | |
'Screen' = @{ | |
'Prompt' = '' | |
'FilterType' = '' | |
'QueryX' = 0 | |
'X' = 0 | |
# 'Y' = 1 # 選択機能があれば... | |
} | |
} | |
$state.Screen.Prompt = Get-Prompt $state $config | |
$state.Screen.FilterType = Get-FilterType $state | |
$state.Screen.QueryX = $Query.Length | |
$state.Screen.X = $state.Screen.Prompt.Length | |
$state.Entry = Get-Entry $state $config | |
$state | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function New-PocoKeymaps | |
{ | |
@{ | |
'Escape' = 'Cancel' | |
'Control+C' = 'Cancel' | |
'Enter' = 'Finish' | |
'Alt+B' = 'BackwardChar' | |
'Alt+F' = 'ForwardChar' | |
'Alt+A' = 'BeginningOfLine' | |
'Alt+E' = 'EndOfLine' | |
'Alt+D8' = 'DeleteBackwardChar' | |
'Backspace' = 'DeleteBackwardChar' | |
'Alt+D' = 'DeleteForwardChar' | |
'Delete' = 'DeleteForwardChar' | |
'Alt+U' = 'KillBeginningOfLine' | |
'Alt+K' = 'KillEndOfLine' | |
'Alt+R' = 'RotateMatcher' | |
'Alt+C' = 'ToggleCaseSensitive' | |
'Alt+I' = 'ToggleInvertFilter' | |
'Alt+W' = 'DeleteBackwardWord' | |
'Alt+N' = 'SelectUp' | |
'Alt+P' = 'SelectDown' | |
'Control+Spacebar' = 'ToggleSelectionAndSelectNext' | |
'UpArrow' = 'SelectUp' | |
'DownArrow' = 'SelectDown' | |
'RightArrow' = 'ScrollPageUp' | |
'LeftArrow' = 'ScrollPageDown' | |
'Tab' = 'TabExpansion' | |
} | |
} | |
function Get-PocoKey | |
{ | |
$flag = [console]::TreatControlCAsInput | |
[console]::TreatControlCAsInput = $true | |
$Key = [console]::ReadKey($true) | |
[console]::TreatControlCAsInput = $flag | |
$KeyString = $Key.Key.ToString() | |
if ($Key.Modifiers -ne 0) | |
{ | |
$m = $Key.Modifiers.ToString() -replace ', ', '+' | |
$KeyString = "${m}+${KeyString}" | |
} | |
return $Key, $KeyString | |
} | |
function Get-Action ($config, $keystr) | |
{ | |
if ($config.Keymaps.Contains($keystr)) | |
{ | |
return $config.Keymaps[$keystr] | |
} | |
if ($keystr -notmatch 'Alt|Control') | |
{ | |
'AddChar' | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Where-Query | |
{ | |
Param | |
( | |
$state, | |
[Parameter(ValueFromPipeline=$True)] | |
$obj | |
) | |
begin {$hash = Convert-QueryHash $state} | |
process | |
{ | |
if ($hash.Contains('')) | |
{ | |
foreach ($value in $hash['']) | |
{ | |
$test = Test-Matching $state.Screen.FilterType $obj $value | |
if ($test -eq $false) {return} | |
} | |
} | |
foreach ($property in $hash.Keys) | |
{ | |
if ($property -eq '') {continue} | |
$l = ($obj | Get-Member $property).length | |
if ($l -eq 0) {continue} | |
foreach ($value in $hash[$property]) | |
{ | |
$test = Test-Matching $state.Screen.FilterType $obj.$property $value | |
if ($test -eq $false) {return} | |
} | |
} | |
,$obj | |
} | |
} | |
function Convert-QueryHash ($state) | |
{ | |
$property = '' | |
$hash = @{$property = @()} | |
$state.Query -split ' ' | ?{$_ -ne ''} | %{ | |
$token = $_ | |
if ($token.StartsWith(':')) | |
{ | |
$property = $token.Remove(0, 1) | |
if (-not $hash.Contains($property)) | |
{ | |
$hash[$property] = @() | |
} | |
} | |
else | |
{ | |
$hash[$property] += $token | |
} | |
} | |
$hash | |
} | |
function Test-Matching | |
{ | |
Param | |
( | |
[string] $FilterType, | |
[string] $p, | |
[string] $value | |
) | |
try | |
{ | |
switch ($FilterType) | |
{ | |
'match' {$p -match $value} | |
'like' {$p -like $value} | |
'eq' {$p -eq $value} | |
'notmatch' {$p -notmatch $value} | |
'notlike' {$p -notlike $value} | |
'neq' {$p -ne $value} | |
'cmatch' {$p -cmatch $value} | |
'clike' {$p -clike $value} | |
'ceq' {$p -ceq $value} | |
'cnotmatch' {$p -cnotmatch $value} | |
'cnotlike' {$p -cnotlike $value} | |
'cneq' {$p -cne $value} | |
} | |
} | |
catch | |
{ | |
$true | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://d.hatena.ne.jp/newpops/20080514 | |
# スクリーンバッファのバックアップ | |
function Backup-ScrBuf | |
{ | |
$rui = Get-RawUI | |
$rect = New-Object System.Management.Automation.Host.Rectangle | |
$rect.Left = 0 | |
$rect.Top = 0 | |
$rect.Right = $rui.WindowSize.Width | |
$rect.Bottom = $rui.CursorPosition.Y | |
$script:screen = $rui.GetBufferContents($rect) | |
} | |
# http://d.hatena.ne.jp/newpops/20080515 | |
# スクリーンバッファのリストア | |
function Restore-ScrBuf | |
{ | |
Clear-Host | |
$rui = Get-RawUI | |
if (-not (Test-Path 'variable:screen')) {return} | |
$origin = New-Object System.Management.Automation.Host.Coordinates(0, 0) | |
$rui.SetBufferContents($origin, $script:screen) | |
$pos = New-Object System.Management.Automation.Host.Coordinates(0, $script:screen.GetUpperBound(0)) | |
$rui.CursorPosition = $pos | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Write-Screen ($state, $config) | |
{ | |
switch ($config.Layout) | |
{ | |
'TopDown' {Write-TopDown $state $config} | |
'BottomUp' {Write-BottomUp $state $config} | |
} | |
} | |
function Write-TopDown ($state, $config) | |
{ | |
Write-ScreenLine 0 $state.Screen.Prompt | |
Write-RightInfo 0 $state | |
if ($state.Entry.length -ne $state.PrevLength) | |
{ | |
$h = (Get-RawUI).WindowSize.Height | |
$entries = $state.Entry | Format-Table | Out-String -Stream | Select-Object -First ($h - 1) | |
if ($entries -is [string]) {$entries = ,@($entries)} | |
foreach ($i in 0..($h - 2)) | |
{ | |
$line = if ($i -lt $entries.length) {$entries[$i]} else {''} | |
Write-ScreenLine ($i + 1) $line | |
} | |
$state.PrevLength = $state.Entry.length | |
} | |
$x = Convert-CursorPositionX $state | |
Set-CursorPosition $x 0 | |
} | |
function Write-BottomUp ($state, $config, $entries) | |
{ | |
if ($state.Entry.length -ne $state.PrevLength) | |
{ | |
if ($state.Screen.Page -gt $m) {$state.Screen.Page = $m} | |
$h = (Get-RawUI).WindowSize.Height | |
$entries = $state.Entry | Format-Table | Out-String -Stream | Select-Object -First ($h - 1) | |
if ($entries -is [string]) {$entries = ,@($entries)} | |
foreach ($i in 0..($h - 2)) | |
{ | |
$line = if ($i -lt $entries.length) {$entries[$i]} else {''} | |
Write-ScreenLine $i $line | |
} | |
$state.PrevLength = $state.Entry.length | |
} | |
Write-ScreenLine ($h - 1) $state.Screen.Prompt | |
Write-RightInfo ($h - 1) $state | |
$x = Convert-CursorPositionX $state | |
$y = (Get-RawUI).CursorPosition.Y | |
Set-CursorPosition $x $y | |
} | |
function Write-ScreenLine ($i, $line) | |
{ | |
$w = (Get-RawUI).BufferSize.Width | |
Set-CursorPosition 0 $i | |
([string]$line).PadRight($w) | Write-Host -NoNewline | |
} | |
function Write-RightInfo ($i, $state) | |
{ | |
$f = $state.Screen.FilterType | |
$n = $state.Entry.Length | |
$h = (Get-RawUI).WindowSize.Height | |
$info = "${f} [${n}]" | |
$w = (Get-RawUI).WindowSize.Width | |
Set-CursorPosition ($w - $info.length) $i | |
$info | Write-Host -NoNewline | |
} | |
function Convert-CursorPositionX ($state) | |
{ | |
$str = $state.Screen.Prompt.Substring(0, $state.Screen.X) | |
(Get-RawUI).LengthInBufferCells($str) | |
} | |
function Set-CursorPosition ($x, $y) | |
{ | |
$pos = New-Object System.Management.Automation.Host.Coordinates($x, $y) | |
(Get-RawUI).CursorPosition = $pos | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Update-State ($state, $config, $action, $key) | |
{ | |
switch ($action) | |
{ | |
'AddChar' {Add-Char $state $config $key.KeyChar} | |
'ForwardChar' {Move-ForwardChar $state} | |
'BackwardChar' {Move-BackwardChar $state} | |
'BeginningOfLine' {Move-BeginningOfLine $state} | |
'EndOfLine' {Move-EndOfLine $state} | |
'DeleteBackwardChar' {Remove-BackwardChar $state} | |
'DeleteForwardChar' {Remove-ForwardChar $state} | |
'KillBeginningOfLine' {Remove-HeadLine $state} | |
'KillEndOfLine' {Remove-TailLine $state} | |
'RotateMatcher' {Select-Matcher $state} | |
'ToggleCaseSensitive' {Switch-CaseSensitive $state} | |
'ToggleInvertFilter' {Switch-InvertFilter $state} | |
default {} # None, Cancel, Finish = identity | |
} | |
$state | |
} | |
function Add-Char ($state, $config, $char) | |
{ | |
$x = $state.Screen.QueryX | |
$q = $state.Query | |
$state.Query = $q.Insert($x, $char) | |
$state.Screen.QueryX++ | |
$state.Screen.X++ | |
$state.Screen.Prompt = Get-Prompt $state $config | |
$state.Entry = Get-Entry $state $config | |
} | |
function Move-BackwardChar ($state) | |
{ | |
$x = $state.Screen.QueryX | |
if ($x - 1 -ge 0) | |
{ | |
$state.Screen.QueryX-- | |
$state.Screen.X-- | |
} | |
} | |
function Move-ForwardChar ($state) | |
{ | |
$x = $state.Screen.X | |
$l = $state.Screen.Prompt.length | |
if ($x + 1 -le $l) | |
{ | |
$state.Screen.QueryX++ | |
$state.Screen.X++ | |
} | |
} | |
function Move-BeginningOfLine ($state) | |
{ | |
$state.Screen.X -= $state.Screen.QueryX | |
$state.Screen.QueryX = 0 | |
} | |
function Move-EndOfLine ($state) | |
{ | |
$state.Screen.QueryX = $state.Query.length | |
$state.Screen.X = $state.Screen.Prompt.length | |
} | |
function Remove-BackwardChar ($state) | |
{ | |
$x = $state.Screen.QueryX | |
$q = $state.Query | |
if ($x - 1 -ge 0) { | |
$state.Query = $q.Remove($x - 1, 1) | |
$state.Screen.QueryX-- | |
$state.Screen.X-- | |
$state.Screen.Prompt = Get-Prompt $state $config | |
$state.Entry = Get-Entry $state $config | |
} | |
} | |
function Remove-ForwardChar ($state) | |
{ | |
$x = $state.Screen.X | |
$l = $state.Screen.Prompt.length | |
$qx = $state.Screen.QueryX | |
$q = $state.Query | |
if ($x + 1 -le $l) | |
{ | |
$state.Query = $q.Remove($qx, 1) | |
$state.Screen.Prompt = Get-Prompt $state $config | |
$state.Entry = Get-Entry $state $config | |
} | |
} | |
function Remove-HeadLine ($state) | |
{ | |
while ($state.Screen.QueryX -gt 0) | |
{ | |
Remove-BackwardChar ($state) | |
} | |
} | |
function Remove-TailLine ($state) | |
{ | |
while ($state.Screen.QueryX -lt $state.Query.length) | |
{ | |
Remove-ForwardChar ($state) | |
} | |
} | |
function Select-Matcher ($state) | |
{ | |
$arr = @('match', 'like', 'eq') | |
$n = $arr.length | |
$i = $arr.IndexOf($state.Filter) + 1 | |
$state.Filter = $arr[$i % $n] | |
$state.Screen.FilterType = Get-FilterType $state | |
$state.Entry = Get-Entry $state $config | |
} | |
function Switch-CaseSensitive ($state) | |
{ | |
$state.CaseSensitive = -not $state.CaseSensitive | |
$state.Screen.FilterType = Get-FilterType $state | |
$state.Entry = Get-Entry $state $config | |
} | |
function Switch-InvertFilter ($state) | |
{ | |
$state.InvertFilter = -not $state.InvertFilter | |
$state.Screen.FilterType = Get-FilterType $state | |
$state.Entry = Get-Entry $state $config | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Get-RawUI {(Get-Host).UI.RawUI} | |
function Get-Prompt ($state, $config) | |
{ | |
$config.Prompt + '> ' + $state.Query | |
} | |
function Get-FilterType ($state) | |
{ | |
$type = '' | |
if ($state.CaseSensitive) {$type += 'c'} | |
if ($state.InvertFilter) {$type += 'not'} | |
$type += $state.Filter | |
$type -replace 'noteq', 'neq' | |
} | |
function Get-Entry ($state, $config) | |
{ | |
$config.Input | | |
Select-Object -Property $config.Property | | |
Where-Query $state | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment