PSAliasFinder.psm1
# ============================================ # PSAliasFinder - PowerShell Alias Discovery Module # Based on the oh-my-zsh alias-finder plugin # ============================================ # ---------------------------- # Function: CountActualPipes # ---------------------------- function CountActualPipes { <# .SYNOPSIS Counts the actual number of pipes in a PowerShell command. .DESCRIPTION Uses the PowerShell Abstract Syntax Tree (AST) to accurately count pipes in a command, ignoring pipes within strings. .PARAMETER Command The command string to analyze. .EXAMPLE CountActualPipes "Get-Process | Where-Object Name -eq 'pwsh'" Returns: 1 #> [CmdletBinding()] param([string]$Command) try { $ast = [System.Management.Automation.Language.Parser]::ParseInput($Command, [ref]$null, [ref]$null) $pipelineAsts = $ast.FindAll({ param($node) $node -is [System.Management.Automation.Language.PipelineAst] }, $true) if ($pipelineAsts.Count -gt 0) { return ($pipelineAsts[0].PipelineElements.Count - 1) } return 0 } catch { return 0 } } # ---------------------------- # Function: ShouldShowAliasSuggestion # ---------------------------- function ShouldShowAliasSuggestion { <# .SYNOPSIS Determines if an alias suggestion should be shown. .DESCRIPTION Applies intelligent filtering to avoid showing suggestions for: - Short commands (less than 8 characters) - Complex commands (multiple pipes or many arguments) - Aliases that don't save enough characters .PARAMETER OriginalCommand The original command entered by the user. .PARAMETER Alias The alias object to evaluate. .EXAMPLE ShouldShowAliasSuggestion "Get-Process" $aliasObject #> [CmdletBinding()] param( [string]$OriginalCommand, [PSCustomObject]$Alias ) $firstCommand = ($OriginalCommand -split '\|')[0].Trim() # Selective criteria if ($firstCommand.Length -lt 8) { return $false } $pipeCount = CountActualPipes $OriginalCommand $argumentCount = ($OriginalCommand -split '\s+').Count if ($pipeCount -gt 1 -or $argumentCount -gt 10) { return $false } $absoluteSaving = $firstCommand.Length - $Alias.Name.Length if ($absoluteSaving -lt 4) { return $false } return $true } # ---------------------------- # Function: Find-Alias # ---------------------------- function Find-Alias { <# .SYNOPSIS Finds aliases for a given PowerShell command. .DESCRIPTION Searches for existing aliases that match the specified command. Supports multiple search modes and filtering options. .PARAMETER Command The command to search aliases for. Accepts multiple words. .PARAMETER Exact Find only exact matches for the command. .PARAMETER Longer Include aliases that are longer than the original command. .PARAMETER Cheaper Only show aliases that are shorter than the original command. .PARAMETER Quiet Suppress console output, only return results. .PARAMETER Force Bypass intelligent filtering and show all matches. .EXAMPLE Find-Alias Get-ChildItem Finds aliases for Get-ChildItem (e.g., gci, ls, dir) .EXAMPLE Find-Alias "Get-Process" -Exact Finds only exact matches for Get-Process .EXAMPLE Find-Alias "docker ps" -Force Shows all aliases for docker ps, bypassing filters #> [CmdletBinding()] param ( [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)] [string[]]$Command, [switch]$Exact, [switch]$Longer, [switch]$Cheaper, [switch]$Quiet, [switch]$Force ) $fullCommand = ($Command -join ' ').Trim() if ([string]::IsNullOrWhiteSpace($fullCommand)) { return @() } $foundAliases = @() $currentCmd = $fullCommand while (-not [string]::IsNullOrWhiteSpace($currentCmd)) { # Search for matching aliases $matchingAliases = Get-Alias | Where-Object { if ($Exact) { $_.Definition -eq $currentCmd } elseif ($Longer) { $_.Definition -like "*$currentCmd*" } else { $_.Definition -eq $currentCmd -or ($currentCmd.StartsWith($_.Definition) -and $currentCmd.Length -gt $_.Definition.Length -and $currentCmd[$_.Definition.Length] -match '\s') } } | ForEach-Object { [PSCustomObject]@{ Name = $_.Name Definition = $_.Definition } } if ($Cheaper) { $matchingAliases = $matchingAliases | Where-Object { $_.Name.Length -lt $fullCommand.Length } } foreach ($alias in $matchingAliases) { if ($foundAliases.Name -notcontains $alias.Name) { $foundAliases += $alias } } if ($Exact -or $Longer) { break } $words = $currentCmd.Trim() -split '\s+' if ($words.Count -le 1) { break } $currentCmd = ($words[0..($words.Count-2)] -join ' ').Trim() } # Apply selective criteria if (-not $Force) { $foundAliases = $foundAliases | Where-Object { ShouldShowAliasSuggestion $fullCommand $_ } } # Display results if (-not $Quiet -and $foundAliases.Count -gt 0) { $foundAliases | ForEach-Object { Write-Host "$($_.Name) -> $($_.Definition)" -ForegroundColor Green } } return $foundAliases } # ---------------------------- # Function: Test-CommandAlias # ---------------------------- function Test-CommandAlias { <# .SYNOPSIS Tests if a command has an available alias and suggests it. .DESCRIPTION Internal function used by the Enter key hook to automatically suggest aliases when commands are entered. .PARAMETER Command The command to test for available aliases. .EXAMPLE Test-CommandAlias "Get-ChildItem" #> [CmdletBinding()] param([Parameter(Mandatory=$true)][string]$Command) try { $cleanCommand = $Command.Trim() -replace '\s+', ' ' if ([string]::IsNullOrWhiteSpace($cleanCommand)) { return } $firstToken = ($cleanCommand -split '\s+')[0] # Count real pipes (not inside strings) $pipeCount = CountActualPipes $cleanCommand # Criteria: long command, max 1 pipe, not already an alias if ($firstToken.Length -ge 8 -and $pipeCount -le 1 -and -not (Get-Alias -Name $firstToken -ErrorAction SilentlyContinue)) { $aliasMatches = Get-Alias | Where-Object { $_.Definition -eq $firstToken } if ($aliasMatches -and ($firstToken.Length - $aliasMatches[0].Name.Length) -ge 4) { Write-Host "`nFound existing alias for `"$firstToken`". You should use: " -NoNewline -ForegroundColor Yellow $aliasNames = $aliasMatches | ForEach-Object { "`"$($_.Name)`"" } Write-Host ($aliasNames -join ", ") -ForegroundColor Magenta } } } catch { Write-Debug "Error in Test-CommandAlias: $_" } } # ---------------------------- # Function: Set-AliasFinderHook # ---------------------------- function Set-AliasFinderHook { <# .SYNOPSIS Enables or disables the automatic alias detection hook. .DESCRIPTION Configures PSReadLine to automatically detect and suggest aliases when the Enter key is pressed. .PARAMETER Enable Explicitly enable the hook and show confirmation message. .PARAMETER Disable Disable the hook and restore default Enter key behavior. .EXAMPLE Set-AliasFinderHook -Enable Enables automatic alias detection .EXAMPLE Set-AliasFinderHook -Disable Disables automatic alias detection #> [CmdletBinding()] param( [switch]$Enable, [switch]$Disable ) if ($Disable) { Set-PSReadLineKeyHandler -Key Enter -Function AcceptLine Write-Host "Alias finder disabled." -ForegroundColor Yellow return } if (Get-Module PSReadLine -ErrorAction SilentlyContinue) { Set-PSReadLineKeyHandler -Key Enter -BriefDescription "AliasFinder" -ScriptBlock { $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if (-not [string]::IsNullOrWhiteSpace($line)) { Test-CommandAlias -Command $line } [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() } if ($Enable) { Write-Host "Alias finder enabled." -ForegroundColor Green } } else { Write-Warning "PSReadLine module not found. Alias finder hook requires PSReadLine." } } # ---------------------------- # Function: Set-AliasFinderConfig # ---------------------------- function Set-AliasFinderConfig { <# .SYNOPSIS Configures the PSAliasFinder module behavior. .DESCRIPTION Sets global configuration for automatic alias detection. .PARAMETER AutoLoad Enable automatic alias detection on module load. .EXAMPLE Set-AliasFinderConfig -AutoLoad Enables automatic alias detection .EXAMPLE Set-AliasFinderConfig Disables automatic alias detection #> [CmdletBinding()] param([switch]$AutoLoad) $global:PSAliasFinderConfig = @{ AutoLoad = $AutoLoad.IsPresent } if ($AutoLoad) { Set-AliasFinderHook -Enable } else { Set-AliasFinderHook -Disable } } # ---------------------------- # Module Initialization # ---------------------------- # Create aliases for Find-Alias function Set-Alias -Name af -Value Find-Alias -ErrorAction SilentlyContinue Set-Alias -Name alias-finder -Value Find-Alias -ErrorAction SilentlyContinue # Initialize configuration if (-not $global:PSAliasFinderConfig) { $global:PSAliasFinderConfig = @{ AutoLoad = $false } } # Auto-enable hook if configured if ($global:PSAliasFinderConfig.AutoLoad -and (Get-Module PSReadLine -ErrorAction SilentlyContinue)) { Set-AliasFinderHook } # Export module members Export-ModuleMember -Function Find-Alias, Test-CommandAlias, Set-AliasFinderHook, Set-AliasFinderConfig Export-ModuleMember -Alias af, alias-finder |