Public/Shell.ps1
|
Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # The shell layer: what `Invoke-Expression (& { (zoxide init powershell | Out-String) })` sets up, # done by Import-Module. Install-ZoxideHook renders the same init script (Get-ZoInitScript, from # zoxide 0.10.0's templates/powershell.txt) and applies it the way evaluating it would: # # - __zoxide_bin, __zoxide_pwd, __zoxide_cd, __zoxide_hook, __zoxide_z and __zoxide_zi become # global functions with the template's own text. They are created from unbound script blocks, # not as module functions, because PowerShell keeps the `cd -` / `cd +` location history per # session state: code that runs inside a module cannot see or extend the history of the # session that typed `cd`, so a module-bound `z -` would fail. Unbound, they run in the # caller's session state, exactly like the template's functions. Their `zoxide` calls reach # this module through the exported `zoxide` alias. # - The prompt is wrapped once, guarded by $global:__zoxide_hooked, so importing the module # again, or also evaluating the binary's init output before or after, leaves one wrapper. # - The z and zi aliases are set globally, as the template sets them. # # _ZO_HOOK, _ZO_CMD and _ZO_NO_CMD stand in for --hook, --cmd and --no-cmd (Get-ZoShellOption), and # _ZO_ECHO is read when the script is rendered, as `zoxide init` reads it. function Install-ZoxideHook { <# .SYNOPSIS Installs z, zi, the __zoxide_* functions and the prompt hook, as `zoxide init powershell` would. .DESCRIPTION Mirrors evaluating the output of Init::run (cmd/init.rs) over templates/powershell.txt, with _ZO_HOOK, _ZO_CMD and _ZO_NO_CMD in place of --hook, --cmd and --no-cmd. Every call (re)defines the template's global __zoxide_* functions from the rendered text. Unless the hook is none it also sets $global:__zoxide_oldpwd (pwd hook) and, only while $global:__zoxide_hooked is not 1, sets it to 1, keeps the current prompt in $global:__zoxide_prompt_old and replaces the prompt with the template's wrapper. Unless _ZO_NO_CMD is 1 it sets the global aliases <cmd> and <cmd>i (default z and zi). PSZoxide.psm1 calls this on import. .EXAMPLE $env:_ZO_CMD = 'j'; Install-ZoxideHook #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Justification = 'The template keeps its hook state in $global:__zoxide_*; both must share it to hook once.')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalAliases', '', Justification = 'The template defines z and zi with -Scope Global; the binary init output must be able to replace them.')] [CmdletBinding()] param() $options = Get-ZoShellOption $cmd = if ($options.NoCmd) { $null } else { $options.Cmd } # The function bodies never depend on cmd, so they come from a render without it: no user # text reaches the parser that turns the template into global functions. $functions = Get-ZoInitFunction -Script (Get-ZoInitScript -Cmd $null -Hook $options.Hook -Echo (Test-ZoEcho)) foreach ($name in $functions.Keys) { if ($name -ne 'prompt') { Microsoft.PowerShell.Management\Set-Item -Path "function:global:$name" -Value $functions[$name] } } if ($options.Hook -ne 'none') { if ($options.Hook -eq 'pwd') { $global:__zoxide_oldpwd = __zoxide_pwd } $global:__zoxide_hooked = Microsoft.PowerShell.Utility\Get-Variable -Name __zoxide_hooked -Scope Global -ValueOnly -ErrorAction Ignore if ($global:__zoxide_hooked -ne 1) { $global:__zoxide_hooked = 1 $global:__zoxide_prompt_old = $function:prompt Microsoft.PowerShell.Management\Set-Item -Path 'function:global:prompt' -Value $functions['prompt'] } } if ($cmd) { Microsoft.PowerShell.Utility\Set-Alias -Name $cmd -Value __zoxide_z -Option AllScope -Scope Global -Force Microsoft.PowerShell.Utility\Set-Alias -Name "${cmd}i" -Value __zoxide_zi -Option AllScope -Scope Global -Force } } |