使用哈希 table 优化函数创建和后续自动完成配置

Optimize function creation and subsequent autocomplete configuration with a hash table

主要问题

我的问题是是否可以使用散列优化函数的创建和后续的自动完成设置 table。

我要优化的功能,例如在下面的散列 table 上加上 ForEach

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}

我为我创建的 wu 函数调整了 -CommandName"$Local:ast".Replace('wu ', 'winget upgrade ')" 的后续自动完成设置,需要针对上面的每个其他 Function 进行调整。原始自动完成的来源是 https://github.com/microsoft/winget-cli/blob/master/doc/Completion.md

据我所知,我需要为每个 Function 创建一个。我想优化它,例如还有一个 ForEach 电话:

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', $_)
        }
}

这是我创建的散列 table,我无法正确使用它们来创建 Functions 和自动完成配置:

$functions = @{
    "wu" = "winget upgrade --include-unknown";
    "wui" = "winget upgrade -i -e";
    "wi" = "winget install -i";
    "wii" = "winget install -i -e";
    "ws" = "winget search"
}

我尝试过这样的方法来创建函数,但它没有计算表达式:

ForEach($item in $functions.keys) {                                                   
    [Microsoft.PowerShell.PSConsoleReadLine]::Insert("Function $item ([string]$PackageName) {$($functions[$item])}")
    [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}


次要问题(已回答)

我还有两个代码块用于自动完成部分。一个用于原始 winget 命令(使用 -CommandName winget--commandline "$Local:ast"),一个用于我的散列 table 命令(使用 -CommandName $funcName--commandline "$Local:new_ast"。可以我巧妙地将它们组合成一个代码块:

# 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', $_)
        }
}

我认为这样的事情可能会起作用,因为 winget 会被正则表达式忽略,因为它不在函数散列 table 中。但我无法弄清楚语法,因为类型是 KeyCollection:

foreach($funcName in $($functions.PSBase.Keys + @{'winget'}))


回答

感谢 @mathias-r-jessen:

$PROFILE 的最终结果
$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', $_)
        }
    }
}

以编程方式注册函数就像在 functions: 驱动器中创建新项目一样简单:

$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 $functions[$funcName]
}

这将注册 table 中的所有 key-value 条目,使用键作为函数名称,并将值作为函数主体的源代码。

确保在全局范围内执行此代码(即,通过从配置文件脚本调用注册代码,或者如果是模块的一部分,则显式导出生成的函数)。

对于参数补全器,使用 -replace 搜索以 w 开头的任何 2 个或 3 个字母的单词,然后使用脚本块作为匹配评估器来查找正确的翻译:

$functions = @{
    "wu" = "winget upgrade --include-unknown";
    "wui" = "winget upgrade -i -e";
    "wi" = "winget install -i";
    "wii" = "winget install -i -e";
    "ws" = "winget search"
}

$commandLine = 'wu somePackage'

$commandLine -replace '^w[uis]{1,2}\b', {
  if($functions.ContainsKey($_.Value)){
    return $functions[$_.Value]
  }

  return $_.Value
}

$translated 现在保存字符串值 "winget upgrade --include-unknown somePackage"


在 6.1 之前的 PowerShell 版本中,您可以通过直接调用 [regex]::Replace():

来实现相同的目的
$functions = @{
    "wu" = "winget upgrade --include-unknown";
    "wui" = "winget upgrade -i -e";
    "wi" = "winget install -i";
    "wii" = "winget install -i -e";
    "ws" = "winget search"
}

$commandLine = 'wu somePackage'

$translated = [regex]::Replace($commandLine, '^w[uis]{1,2}\b', {
  param($match)

  if($functions.ContainsKey($match.Value)){
    return $functions[$match.Value]
  }

  return $match.Value
})