Tools/psreadline.ps1
|
# Companion for psreadline — apply settings, theme, and register theme picker (fprl) # 1. Apply settings from tool JSON $_settings = $DFCurrentTool.PSObject.Properties['settings']?.Value if ($_settings) { $_settingsMap = @{ editMode = 'EditMode' predictionSource = 'PredictionSource' predictionViewStyle = 'PredictionViewStyle' bellStyle = 'BellStyle' historyNoDuplicates = 'HistoryNoDuplicates' historySaveStyle = 'HistorySaveStyle' } $_optionArgs = @{} $_settings.PSObject.Properties | ForEach-Object { $_param = $_settingsMap[$_.Name] if ($_param) { $_optionArgs[$_param] = $_.Value } else { Write-Warning "DotForge: unknown PSReadLine setting '$($_.Name)' — skipping" } } if ($_optionArgs.Count -gt 0) { try { Set-PSReadLineOption @_optionArgs } catch [System.ArgumentException] { # PredictionSource/PredictionViewStyle throw when VT is unavailable # (e.g. redirected output in tests). Apply the remaining options individually. foreach ($_k in @($_optionArgs.Keys)) { $_single = [hashtable]::new() $_single[$_k] = $_optionArgs[$_k] try { Set-PSReadLineOption @_single } catch [System.ArgumentException] { Write-Warning "DotForge: PSReadLine option '$_k' not supported in this terminal — skipping" } } } } } # 2. Register Invoke-DFApplyPSReadLineTheme (captures $_bundledDir via closure) $_bundledDir = Join-Path $PSScriptRoot 'psreadline' Set-Item -Path 'function:global:Invoke-DFApplyPSReadLineTheme' -Value ({ [CmdletBinding()] param([Parameter(Mandatory)][string]$Name) # Resolve path: absolute path passthrough, XDG user dir, then bundled $path = $null if ([System.IO.Path]::IsPathRooted($Name)) { $path = $Name } else { if ($Env:XDG_CONFIG_HOME) { $p = Join-Path $Env:XDG_CONFIG_HOME 'psreadline' 'themes' "$Name.json" if (Test-Path $p) { $path = $p } } if (-not $path) { $p = Join-Path $_bundledDir "$Name.json" if (Test-Path $p) { $path = $p } } } if (-not $path) { Write-Warning "DotForge: PSReadLine theme '$Name' not found" return } $theme = Get-Content $path -Raw | ConvertFrom-Json $colors = @{} $theme.colors.PSObject.Properties | ForEach-Object { $hex = $_.Value if ($hex -match '^#[0-9A-Fa-f]{6}$') { $r = [Convert]::ToInt32($hex.Substring(1, 2), 16) $g = [Convert]::ToInt32($hex.Substring(3, 2), 16) $b = [Convert]::ToInt32($hex.Substring(5, 2), 16) $colors[$_.Name] = "`e[38;2;${r};${g};${b}m" } else { Write-Warning "DotForge: invalid color '$hex' for token '$($_.Name)' — skipping" } } if ($colors.Count -gt 0) { # Expose applied colors for testing in non-VT environments where # Get-PSReadLineOption.Colors returns $null (redirected output). $global:DFPSReadLineColors = $colors Set-PSReadLineOption -Colors $colors } }.GetNewClosure()) # 3. Apply initial theme $_themeSetting = $null if ($null -ne (Get-Variable -Name DFConfig -Scope Global -ErrorAction Ignore)) { $_themeSetting = $Global:DFConfig['PSReadLineTheme'] } Invoke-DFApplyPSReadLineTheme -Name ($_themeSetting ?? 'dark') # 4. Register theme picker Set-Item -Path 'function:global:Select-PSReadLineTheme' -Value ({ [CmdletBinding()] param() $themes = [System.Collections.Generic.List[string]]::new() $seen = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::OrdinalIgnoreCase) if ($Env:XDG_CONFIG_HOME) { $userDir = Join-Path $Env:XDG_CONFIG_HOME 'psreadline' 'themes' if (Test-Path $userDir) { Get-ChildItem $userDir -Filter '*.json' | Sort-Object Name | ForEach-Object { if ($seen.Add($_.BaseName)) { $themes.Add($_.BaseName) } } } } if (Test-Path $_bundledDir) { Get-ChildItem $_bundledDir -Filter '*.json' | Sort-Object Name | ForEach-Object { if ($seen.Add($_.BaseName)) { $themes.Add($_.BaseName) } } } if ($themes.Count -eq 0) { Write-Warning 'DotForge: no PSReadLine themes found' return } Invoke-DFPicker ` -List { $themes }.GetNewClosure() ` -Header 'Select PSReadLine theme [Enter to apply for this session]' ` -Action { param($n) Invoke-DFApplyPSReadLineTheme -Name $n Write-Host "Theme applied: $n (to persist: set `$Global:DFConfig['PSReadLineTheme'] = '$n')" -ForegroundColor Green } }.GetNewClosure()) Set-Alias -Name fprl -Value Select-PSReadLineTheme -Scope Global -Force |