ble.psm1
|
# ble.ps1 - a ble.sh-like line editor for PowerShell. # # Import-Module ble # load and attach # Import-Module ble -ArgumentList noattach; ble-attach # param([string]$AttachMode = 'attach') # ---- default history file (shared with PSReadLine) -------------------------- $__bleHistory = $null try { # only ask PSReadLine if it is already loaded: importing it just for this path costs ~90 ms if (Get-Module PSReadLine) { $__bleHistory = (Get-PSReadLineOption).HistorySavePath } } catch { } if (-not $__bleHistory) { $__bleHost = if ($Host -and $Host.Name) { $Host.Name } else { 'ConsoleHost' } if ($IsWindows -or $env:OS -eq 'Windows_NT') { $__bleHistory = Join-Path $env:APPDATA "Microsoft\Windows\PowerShell\PSReadLine\${__bleHost}_history.txt" } else { $__bleData = if ($env:XDG_DATA_HOME) { $env:XDG_DATA_HOME } else { Join-Path $HOME '.local/share' } $__bleHistory = Join-Path $__bleData "powershell/PSReadLine/${__bleHost}_history.txt" } } [Ble.Session]::DefaultHistoryFile = $__bleHistory Remove-Variable __bleHistory, __bleHost, __bleData -ErrorAction Ignore # ---- hooks ----------------------------------------------------------------- $script:BleAttached = $false $script:BleOrigReadLine = $null $script:BleOrigPrompt = $null # Wraps the user's prompt: prints ble status marks first and records the prompt text. # $? is captured first and re-established before calling the original prompt. $script:BlePromptWrapper = { $__ble_status = $? [Ble.Session]::BeforePrompt($__ble_status, $global:LASTEXITCODE) if (-not $__ble_status) { Write-Error -Message '' -ErrorAction Ignore } $__ble_out = & $script:BleOrigPrompt [Ble.Session]::AfterPrompt($__ble_out) $__ble_out } $script:BleReadLine = { $__ble_lastRunStatus = $? if ($script:BleAttached) { $__ble_p = Get-Item function:prompt -ErrorAction Ignore if ($__ble_p -and -not $__ble_p.ScriptBlock.ToString().Contains('__ble_status')) { # the user (or a prompt framework) replaced the prompt after attaching $script:BleOrigPrompt = $__ble_p.ScriptBlock Set-Item function:global:prompt -Value $script:BlePromptWrapper } } [Ble.Session]::ReadLine($ExecutionContext, $__ble_lastRunStatus) } function Connect-Ble { <# .SYNOPSIS Attaches ble.ps1 to the console host (ble-attach). #> [CmdletBinding()] param() if ($script:BleAttached) { return } $rl = Get-Item function:PSConsoleHostReadLine -ErrorAction Ignore $script:BleOrigReadLine = if ($rl) { $rl.ScriptBlock } else { $null } $p = Get-Item function:prompt -ErrorAction Ignore $script:BleOrigPrompt = if ($p) { $p.ScriptBlock } else { { "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) " } } Set-Item function:global:prompt -Value $script:BlePromptWrapper Set-Item function:global:PSConsoleHostReadLine -Value $script:BleReadLine $script:BleAttached = $true } function Disconnect-Ble { <# .SYNOPSIS Detaches ble.ps1 and restores the previous line editor (ble-detach). #> [CmdletBinding()] param() if (-not $script:BleAttached) { return } if ($script:BleOrigReadLine) { Set-Item function:global:PSConsoleHostReadLine -Value $script:BleOrigReadLine } else { Remove-Item function:global:PSConsoleHostReadLine -ErrorAction Ignore } if ($script:BleOrigPrompt) { Set-Item function:global:prompt -Value $script:BleOrigPrompt } $script:BleAttached = $false } function Test-BleAttached { $script:BleAttached } # like ble.sh's $BLE_VERSION. Ble.dll is built with ModuleVersion from ble.psd1 (the module's # own Version is still 0.0 while this file loads) $global:BLE_VERSION = [Ble.Session].Assembly.GetName().Version.ToString(3) function Invoke-Ble { <# .SYNOPSIS ble.sh's `ble` command: ble version, ble attach, ble bind ..., ble opt ..., ble face ... Without arguments it returns whether ble.ps1 is attached. #> if (-not $args) { return $script:BleAttached } $cmd = $args[0] $rest = @($args | Select-Object -Skip 1) switch -Exact ($cmd) { { $_ -in 'version', '--version' } { "ble.ps1, version $global:BLE_VERSION"; return } { $_ -in 'help', '--help' } { 'usage: ble [SUBCOMMAND [ARGS...]]' 'subcommands: attach detach bind opt face sabbrev import rc history version help' return } 'opt' { bleopt @rest; return } } # like ble.sh: any other word runs ble-<word> if it exists if ($cmd -match '^[-a-zA-Z0-9]+$' -and (Get-Command "ble-$cmd" -ErrorAction Ignore)) { & "ble-$cmd" @rest; return } Write-Error "ble (ble.ps1): unrecognized subcommand '$cmd'." } Set-Alias -Name ble-attach -Value Connect-Ble Set-Alias -Name ble-detach -Value Disconnect-Ble Set-Alias -Name ble -Value Invoke-Ble $ExecutionContext.SessionState.Module.OnRemove = { Disconnect-Ble } Export-ModuleMember -Function Connect-Ble, Disconnect-Ble, Test-BleAttached, Invoke-Ble -Alias * -Cmdlet * if ($AttachMode -ne 'noattach' -and $env:BLE_ATTACH -ne 'none') { if ([Environment]::UserInteractive -and -not [Console]::IsInputRedirected) { Connect-Ble } } |