Primary Question
My question is whether it is possible to optimize the creation of functions and subsequent autocomplete settings with a hash table.
The Functions I want to optimize e.g. with a ForEach over the hash table below:
Function wu([string]$PackageName) {winget upgrade --include-unknown $PackageName}
Function wui([string]$PackageName) {winget upgrade -i $PackageName}
Function wi([string]$PackageName) {winget install $PackageName}
Function wii([string]$PackageName) {winget install -i -e $PackageName}
Function ws([string]$PackageName) {winget search $PackageName}
The subsequent autocomplete settings where I have adjusted -CommandName and "$Local:ast".Replace('wu ', 'winget upgrade ')" for the wu function I created, need to be adjusted for every other Function above. Source for original autocomplete is https://github.com/microsoft/winget-cli/blob/master/doc/Completion.md
As far as I understand I would need to create this for every single Function. I'd like to optimize that e.g. with a ForEachcall as well:
Register-ArgumentCompleter -Native -CommandName wu -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
$Local:word = $wordToComplete.Replace('"', '""')
$Local:ast = $commandAst.ToString().Replace('"', '""')
winget complete --word="$Local:word" --commandline "$Local:ast".Replace('wu ', 'winget upgrade ') --position $cursorPosition | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
This is the hash table I created where I'm not able to properly use them for the creation of the Functions and autocomplete configurations:
$functions = @{
"wu" = "winget upgrade --include-unknown";
"wui" = "winget upgrade -i -e";
"wi" = "winget install -i";
"wii" = "winget install -i -e";
"ws" = "winget search"
}
I tried something like this for creating functions, but it didn't evaluate the expressions:
ForEach($item in $functions.keys) {
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("Function $item ([string]$PackageName) {$($functions[$item])}")
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}
Secondary question (Answered)
I still have two code blocks for the autocomplete section. One for the original winget command (with -CommandName winget and --commandline "$Local:ast") and one for my hash table commands (with -CommandName $funcName and --commandline "$Local:new_ast". Can I neatly combine them into one code blocks:
# Hashtable autocomplete configuration
foreach($funcName in $functions.PSBase.Keys){
Register-ArgumentCompleter -Native -CommandName $funcName -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
$Local:word = $wordToComplete.Replace('"', '""')
$Local:ast = $commandAst.ToString().Replace('"', '""')
$Local:new_ast = $Local:ast -replace '^w[uis]{1,2}\b', {
if($functions.ContainsKey($_.Value)){ return $functions[$_.Value] } return $_.Value
}
winget complete --word="$Local:word" --commandline "$Local:new_ast" --position $cursorPosition | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
}
# Original autocomplete configuration
Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
$Local:word = $wordToComplete.Replace('"', '""')
$Local:ast = $commandAst.ToString().Replace('"', '""')
winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
I think something like this might work, because winget would be ignored by the regex since it's not in the functions hash table. But I can't figure out the syntax because the type is KeyCollection:
foreach($funcName in $($functions.PSBase.Keys + @{'winget'}))
Answer
The finished result in my $PROFILE thanks to @mathias-r-jessen:
$functions = @{
"wu" = "winget upgrade --include-unknown";
"wui" = "winget upgrade -i -e";
"wi" = "winget install -i";
"wii" = "winget install -i -e";
"ws" = "winget search"
}
foreach($funcName in $functions.PSBase.Keys){
New-Item function:\ -Name $funcName -Value $([scriptblock]::Create($functions[$funcName])) | Out-Null
}
foreach($funcName in @($functions.PSBase.Keys; 'winget')){
Register-ArgumentCompleter -Native -CommandName $funcName -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
$Local:word = $wordToComplete.Replace('"', '""')
$Local:ast = $commandAst.ToString().Replace('"', '""')
$Local:new_ast = $Local:ast -replace '^w[uis]{1,2}\b', {
if($functions.ContainsKey($_.Value)){ return $functions[$_.Value] } return $_.Value
}
winget complete --word="$Local:word" --commandline "$Local:new_ast" --position $cursorPosition | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
}