UltimateCompleter.psm1
|
# Define the path to the JSON data file $DataPath = Join-Path -Path $PSScriptRoot -ChildPath "completions.json" # Load the JSON data into memory (Using -AsHashTable to prevent case collisions like -d vs -D) if (Test-Path $DataPath) { # This requires PowerShell 6+ / 7 $Global:UltimateCompletions = Get-Content -Raw -Path $DataPath | ConvertFrom-Json -AsHashTable } else { Write-Warning "UltimateCompleter: completions.json not found!" return } # Find the root commands in the JSON and register a completer for each foreach ($RootCommand in $Global:UltimateCompletions.Keys) { Register-ArgumentCompleter -CommandName $RootCommand, "$RootCommand.exe" -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) $Elements = $commandAst.CommandElements $RawRoot = $Elements[0].Value # Lowercase the root command to safely find it in the Hashtable $Root = ($RawRoot -replace '(?i)\.exe$', '').ToLower() # Check if root command exists in JSON if (-not $Global:UltimateCompletions.Contains($Root)) { return } $CurrentNode = $Global:UltimateCompletions[$Root] $Results = @() $IsTyping = ![string]::IsNullOrWhiteSpace($wordToComplete) # IGNORE LIST: You can list flags here that cause unnecessary clutter $IgnoredItems = @('--color', '--version', '-v', '--help', '-h') # LEVEL 1: Completing the first argument (subcommands or root flags) if (($Elements.Count -eq 1 -and -not $IsTyping) -or ($Elements.Count -eq 2 -and $IsTyping)) { if ($CurrentNode.Contains('subcommands')) { $Results += $CurrentNode['subcommands'].Keys } if ($CurrentNode.Contains('flags')) { # Check if flags are an array or an object/hashtable containing descriptions if ($CurrentNode['flags'] -is [System.Collections.Hashtable]) { $Results += $CurrentNode['flags'].Keys } else { $Results += $CurrentNode['flags'] } } } # LEVEL 2+: Completing arguments/flags AFTER a subcommand elseif (($Elements.Count -ge 2 -and -not $IsTyping) -or ($Elements.Count -ge 3 -and $IsTyping)) { $SubCommand = $Elements[1].Value.ToLower() if ($CurrentNode.Contains('subcommands') -and $CurrentNode['subcommands'].Contains($SubCommand)) { $SubNode = $CurrentNode['subcommands'][$SubCommand] if ($SubNode.Contains('flags')) { $FlagsNode = $SubNode['flags'] if ($FlagsNode -is [System.Collections.Hashtable]) { $Results += $FlagsNode.Keys } else { $Results += $FlagsNode } } } } # FILTER: Remove ignored items first, then filter by typed characters $Results = $Results | Where-Object { $_ -notin $IgnoredItems } if ($IsTyping) { $Results = $Results | Where-Object { $_ -like "$wordToComplete*" } } # FORMAT: Convert the found results into PowerShell's CompletionResult format $Results | Select-Object -Unique | ForEach-Object { $ItemName = $_ $Tooltip = $ItemName # 1. Add descriptions to subcommands if ($CurrentNode.Contains('subcommands') -and $CurrentNode['subcommands'].Contains($ItemName)) { if ($CurrentNode['subcommands'][$ItemName].Contains('description')) { $Tooltip = $CurrentNode['subcommands'][$ItemName]['description'] } } # 2. Add descriptions to root flags (if formatted as a hashtable) elseif ($CurrentNode.Contains('flags') -and $CurrentNode['flags'] -is [System.Collections.Hashtable] -and $CurrentNode['flags'].Contains($ItemName)) { $Tooltip = $CurrentNode['flags'][$ItemName] } # 3. Add descriptions to subcommand flags (if formatted as a hashtable) elseif ($Elements.Count -ge 2) { $SubCommand = $Elements[1].Value.ToLower() if ($CurrentNode.Contains('subcommands') -and $CurrentNode['subcommands'].Contains($SubCommand)) { $SubNode = $CurrentNode['subcommands'][$SubCommand] if ($SubNode.Contains('flags') -and $SubNode['flags'] -is [System.Collections.Hashtable] -and $SubNode['flags'].Contains($ItemName)) { $Tooltip = $SubNode['flags'][$ItemName] } } } [System.Management.Automation.CompletionResult]::new($ItemName, $ItemName, 'ParameterValue', $Tooltip) } } } |