├── LICENSE ├── README.md ├── README.zh.md └── src ├── scoop-completion.psd1 └── scoop-completion.psm1 /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ala 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scoop-completion 2 | 3 | [中文](https://github.com/Moeologist/scoop-completion/blob/master/README.zh.md) 4 | 5 | --- 6 | 7 | Presuppositions: 8 | * [scoop](https://github.com/lukesampson/scoop) 9 | * [PowerShell 5](https://aka.ms/wmf5download) (or later, include [PowerShell Core](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6)) 10 | 11 | Install via scoop: 12 | ```powershell 13 | # add extras bucket 14 | scoop bucket add extras 15 | 16 | # install 17 | scoop install scoop-completion 18 | ``` 19 | 20 | Enable completion in current shell: 21 | ```powershell 22 | # enable completion in current shell, use absolute path because PowerShell Core not respect $env:PSModulePath 23 | Import-Module "$($(Get-Item $(Get-Command scoop.ps1).Path).Directory.Parent.FullName)\modules\scoop-completion" 24 | ``` 25 | 26 | Auto-load, please modify $Profile manually. If you want completion to work for allusers | allhosts, read [Docs](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-6#the-profile-variable) 27 | ```powershell 28 | # create profile if not exist 29 | if (!(Test-Path $profile)) { New-Item -Path $profile -ItemType "file" -Force } 30 | 31 | # print $profile path 32 | $profile 33 | ``` 34 | Open $profile in your text editor, and put the enable code (Import-Module line) into this file. 35 | 36 | --- 37 | 38 | Usage: 39 | Type "scoop [something]" and press Tab key will cycle completion items, Ctrl+Space will trigger menu-completion. 40 | 41 | Example: 42 | ```powershell 43 | scoop ins[Press Tab] 44 | scoop install py[Press Ctrl+Space] 45 | scoop uninstall [Press Ctrl+Space] 46 | ``` 47 | 48 | --- 49 | 50 | Uninstall: 51 | ```powershell 52 | scoop uninstall scoop-completion 53 | ``` 54 | Then, modify *$Profile* to remove initialization calls 55 | 56 | --- 57 | 58 | Install from PSGallery **(deprecated)**: 59 | ```powershell 60 | Install-Module -AllowClobber -Name scoop-completion -Scope CurrentUser 61 | Import-Module scoop-completion 62 | ``` 63 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | ## scoop 自动补全 2 | 3 | [English](https://github.com/Moeologist/scoop-completion/blob/master/README.md) 4 | 5 | --- 6 | 7 | 依赖: 8 | * [scoop](https://github.com/lukesampson/scoop) 9 | * [PowerShell 5](https://aka.ms/wmf5download) (或更新,包括 [PowerShell Core](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6)) 10 | 11 | 通过 scoop 安装: 12 | ```powershell 13 | # add extras bucket 14 | scoop bucket add extras 15 | 16 | # install 17 | scoop install scoop-completion 18 | ``` 19 | 20 | 在当前 shell 启用补全: 21 | ```powershell 22 | # enable completion in current shell 23 | Import-Module "$($(Get-Item $(Get-Command scoop.ps1).Path).Directory.Parent.FullName)\modules\scoop-completion" 24 | ``` 25 | 26 | 自动加载,请手动修改 $Profile。如果希望补全为 所有用户 | 所有host 工作,请阅读 [Docs](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-6#the-profile-variable) 27 | ```powershell 28 | # create profile if not exist 29 | if (!(Test-Path $profile)) { New-Item -Path $profile -ItemType "file" -Force } 30 | 31 | # print $profile path 32 | $profile 33 | ``` 34 | 在文本编辑器打开 $profile,然后添加启用代码(Import-Module 行)到该文件。 35 | 36 | --- 37 | 38 | 用法: 39 | 输入 "scoop [想补全的内容]" 然后按 Tab 键将循环补全项,Ctrl+Space 将触发菜单式的补全 40 | 41 | 例子: 42 | ```powershell 43 | scoop ins[Press Tab] 44 | scoop install py[Press Ctrl+Space] 45 | scoop uninstall [Press Ctrl+Space] 46 | 47 | ``` 48 | 49 | --- 50 | 51 | 卸载: 52 | ```powershell 53 | scoop uninstall scoop-completion 54 | ``` 55 | 然后手动修改 $Profile (移除启用代码) 56 | 57 | --- 58 | 59 | 通过 PSGallery 安装 **(已弃用)**: 60 | ```powershell 61 | Install-Module -AllowClobber -Name scoop-completion -Scope CurrentUser 62 | Import-Module scoop-completion 63 | ``` 64 | -------------------------------------------------------------------------------- /src/scoop-completion.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Author = 'ala' 3 | Description = 'A Scoop tab completion module for PowerShell.' 4 | 5 | # Script module or binary module file associated with this manifest. 6 | RootModule = 'scoop-completion.psm1' 7 | 8 | # Version number of this module. 9 | ModuleVersion = '0.3.0' 10 | 11 | # ID used to uniquely identify this module 12 | GUID = 'e79be23b-d149-46c8-85a3-620f2669d2e1' 13 | 14 | # Minimum version of the Windows PowerShell engine required by this module 15 | PowerShellVersion = '5.0' 16 | 17 | FunctionsToExport = @() 18 | CmdletsToExport = @() 19 | VariablesToExport = @() 20 | AliasesToExport = @() 21 | 22 | # Private data to pass to the module specified in RootModule/ModuleToProcess. 23 | # This may also contain a PSData hashtable with additional module metadata used by PowerShell. 24 | PrivateData = 25 | @{ 26 | PSData = 27 | @{ 28 | # Tags applied to this module. These help with module discovery in online galleries. 29 | Tags = @('scoop', 'tab', 'completion') 30 | 31 | LicenseUri = 'https://opensource.org/licenses/MIT' 32 | 33 | # A URL to the main website for this project. 34 | ProjectUri = 'https://github.com/Moeologist/scoop-completion' 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/scoop-completion.psm1: -------------------------------------------------------------------------------- 1 | # This source code is licensed under the MIT License 2 | # Project URL - https://github.com/Moeologist/scoop-completion 3 | # Thanks to Posh-Git - https://github.com/dahlbyk/posh-git 4 | 5 | # See scoop/lib/core.ps1 6 | function script:load_cfg($file) { 7 | if (!(Test-Path $file)) { 8 | return $null 9 | } 10 | try { 11 | return (Get-Content $file -Raw | ConvertFrom-Json -ErrorAction Stop) 12 | } 13 | catch { } 14 | } 15 | 16 | $script:configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1 17 | $script:configFile = "$configHome\scoop\config.json" 18 | $script:scoopConfig = load_cfg $script:configFile 19 | 20 | function script:get_config($name, $default) { 21 | if ($null -eq $scoopConfig.$name -and $null -ne $default) { 22 | return $default 23 | } 24 | return $scoopConfig.$name 25 | } 26 | 27 | try { 28 | $Script:scoopdir = $env:SCOOP, (get_config 'root_path'), "$env:USERPROFILE\scoop" | 29 | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1 30 | } 31 | catch { Write-Warning 'No scoop installed!' } 32 | 33 | $script:aliasMap = get_config 'alias' 34 | 35 | # See scoop/libexec/ 36 | $script:ScoopCommands = @( 37 | 'alias', 38 | 'bucket', 39 | 'cache', 40 | 'cat', 41 | 'checkup', 42 | 'cleanup', 43 | 'config', 44 | 'create', 45 | 'depends', 46 | 'download', 47 | 'export', 48 | 'help', 49 | 'hold', 50 | 'home', 51 | 'import', 52 | 'info', 53 | 'install', 54 | 'list', 55 | 'prefix', 56 | 'reset', 57 | 'search', 58 | 'shim', 59 | 'status', 60 | 'unhold', 61 | 'uninstall', 62 | 'update', 63 | 'virustotal', 64 | 'which' 65 | ) 66 | 67 | # See scoop/libexec/scoop-config.ps1 68 | $script:ScoopConfigParams = @( 69 | # 'last_update' would be set by scoop 70 | 'use_external_7zip', 71 | 'use_lessmsi', 72 | 'no_junction', 73 | 'scoop_repo', 74 | 'scoop_branch', 75 | 'proxy', 76 | 'autostash_on_conflict', 77 | 'default_architecture', 78 | 'debug', 79 | 'force_update', 80 | 'show_update_log', 81 | 'show_manifest', 82 | 'shim', 83 | 'root_path', 84 | 'global_path', 85 | 'cache_path', 86 | 'gh_token', 87 | 'virustotal_api_key', 88 | 'cat_style', 89 | 'ignore_running_processes', 90 | 'private_hosts', 91 | 'hold_update_until', 92 | 'aria2-enabled', 93 | 'aria2-warning-enabled', 94 | 'aria2-retry-wait', 95 | 'aria2-split', 96 | 'aria2-max-connection-per-server', 97 | 'aria2-min-split-size', 98 | 'aria2-options' 99 | ) 100 | 101 | $script:ScoopSubcommands = @{ 102 | alias = 'add list rm' 103 | bucket = 'add list known rm' 104 | cache = 'rm show' 105 | config = (@('rm') + $script:ScoopConfigParams) -join ' ' 106 | # add 'rm' to config 107 | shim = 'add list rm info alter' 108 | } 109 | 110 | $script:ScoopShortParams = @{ 111 | install = 'g i k u s a' 112 | uninstall = 'g p' 113 | cleanup = 'g' 114 | virustotal = 'a s n' 115 | update = 'f g i k s q a' 116 | shim = 'g' 117 | download = 'f h u a' 118 | status = 'l' 119 | } 120 | 121 | $script:ScoopLongParams = @{ 122 | install = 'global independent no-cache no-update-scoop skip arch' 123 | uninstall = 'global purge' 124 | cleanup = 'global' 125 | virustotal = 'arch scan no-depends' 126 | update = 'force global independent no-cache skip quiet all' 127 | shim = 'global' 128 | download = 'force no-hash-check no-update-scoop arch' 129 | status = 'local' 130 | } 131 | 132 | $script:ScoopParamValues = @{ 133 | install = @{ 134 | a = '32bit 64bit' 135 | arch = '32bit 64bit' 136 | } 137 | virustotal = @{ 138 | a = '32bit 64bit' 139 | arch = '32bit 64bit' 140 | } 141 | download = @{ 142 | a = '32bit 64bit' 143 | arch = '32bit 64bit' 144 | } 145 | } 146 | 147 | # See scoop/libexec/scoop-config.ps1 148 | # TODO:display explanation of these settings 149 | $script:ScoopConfigParamValues = @{ 150 | use_external_7zip = 'true false' 151 | use_lessmsi = 'true false' 152 | no_junction = 'true false' 153 | # scoop_repo 154 | scoop_branch = 'master develop' 155 | # proxy 156 | autostash_on_conflict = 'true false' 157 | default_architecture = '32bit 64bit' 158 | debug = 'true false' 159 | force_update = 'true false' 160 | show_update_log = 'true false' 161 | show_manifest = 'true false' 162 | shim = 'kiennq scoopcs 71' 163 | # root_path 164 | # global_path 165 | # cache_path 166 | # gh_token 167 | # virustotal_api_key 168 | cat_style = 'default auto full plain changes header header-filename header-filesize grid rule numbers snip' 169 | ignore_running_processes = 'true false' 170 | # private_hosts 171 | # hold_update_until 172 | "aria2-enabled" = 'true false' 173 | "aria2-warning-enabled" = 'true false' 174 | # 'aria2-retry-wait' 175 | # 'aria2-split', 176 | # 'aria2-max-connection-per-server', 177 | # 'aria2-min-split-size', 178 | # 'aria2-options' 179 | } 180 | 181 | $script:ScoopCommandsWithLongParams = $ScoopLongParams.Keys -join '|' 182 | $script:ScoopCommandsWithShortParams = $ScoopShortParams.Keys -join '|' 183 | $script:ScoopCommandsWithParamValues = $ScoopParamValues.Keys -join '|' 184 | 185 | # 6> redirect Write-Host's output, (〒︿〒) 186 | function script:ScoopAlias($filter) { 187 | if ($null -ne $script:aliasMap) { 188 | @($script:aliasMap.PSObject.Properties | Select-Object Name | ForEach-Object { $_.Name } 189 | Where-Object { $_ -like "$filter*" } 190 | ) 191 | } else { 192 | @() 193 | } 194 | } 195 | 196 | function script:ScoopExpandCmdParams($commands, $command, $filter) { 197 | $commands.$command -split ' ' | Where-Object { $_ -like "$filter*" } 198 | } 199 | 200 | function script:ScoopExpandCmd($filter, $includeAliases) { 201 | $cmdList = @() 202 | $cmdList += $ScoopCommands 203 | if ($includeAliases) { 204 | $cmdList += ScoopAlias($filter) 205 | } 206 | $cmdList -like "$filter*" | Sort-Object 207 | } 208 | 209 | function script:ScoopLocalPackages($filter) { 210 | @(& Get-ChildItem -Path $script:scoopdir\apps -Name -Directory | 211 | Where-Object { $_ -ne "scoop" } | 212 | Where-Object { $_ -like "$filter*" } 213 | ) 214 | } 215 | 216 | function script:ScoopConfigParamsFilter($filter) { 217 | $ScoopConfigParams -like "$filter*" 218 | } 219 | 220 | function script:ScoopExpandConfigParamValues($param, $filter) { 221 | $ScoopConfigParamValues[$param] -split ' ' | 222 | Where-Object { $_ -like "$filter*" } | 223 | Sort-Object 224 | } 225 | 226 | function script:ScoopRemotePackages($filter) { 227 | @(& Get-ChildItem -Path $script:scoopdir\buckets\ -Name | 228 | ForEach-Object { Get-ChildItem -Path $script:scoopdir\buckets\$_\bucket -Name -Filter *.json } | 229 | ForEach-Object { if ( $_ -match '^([\w][\-\.\w]*)\.json$' ) { "$($Matches[1])" } } | 230 | Where-Object { $_ -like "$filter*" } 231 | ) 232 | } 233 | 234 | function script:ScoopLocalCaches($filter) { 235 | @(& scoop cache show | 236 | Select-Object -ExpandProperty Name | 237 | Sort-Object -Unique | 238 | Where-Object { $_ -like "$filter*" } 239 | ) 240 | } 241 | 242 | function script:ScoopLocalBuckets($filter) { 243 | @(& scoop bucket list | Where-Object { $_ -like "$filter*" }) 244 | } 245 | 246 | function script:ScoopRemoteBuckets($filter) { 247 | @(& scoop bucket known | Where-Object { $_ -like "$filter*" }) 248 | } 249 | 250 | function script:ScoopExpandLongParams($cmd, $filter) { 251 | $ScoopLongParams[$cmd] -split ' ' | 252 | Where-Object { $_ -like "$filter*" } | 253 | Sort-Object | 254 | ForEach-Object { -join ("--", $_) } 255 | } 256 | 257 | function script:ScoopExpandShortParams($cmd, $filter) { 258 | $ScoopShortParams[$cmd] -split ' ' | 259 | Where-Object { $_ -like "$filter*" } | 260 | Sort-Object | 261 | ForEach-Object { -join ("-", $_) } 262 | } 263 | 264 | function script:ScoopExpandParamValues($cmd, $param, $filter) { 265 | $ScoopParamValues[$cmd][$param] -split ' ' | 266 | Where-Object { $_ -like "$filter*" } | 267 | Sort-Object 268 | } 269 | 270 | function script:ScoopTabExpansion($lastBlock) { 271 | 272 | switch -regex ($lastBlock) { 273 | # Handles Scoop -- 274 | "^(?$ScoopCommandsWithParamValues).* --(?.+) (?\w*)$" { 275 | if ($ScoopParamValues[$matches['cmd']][$matches['param']]) { 276 | return ScoopExpandParamValues $matches['cmd'] $matches['param'] $matches['value'] 277 | } 278 | } 279 | 280 | # Handles Scoop - 281 | "^(?$ScoopCommandsWithParamValues).* -(?.+) (?\w*)$" { 282 | if ($ScoopParamValues[$matches['cmd']][$matches['param']]) { 283 | return ScoopExpandParamValues $matches['cmd'] $matches['param'] $matches['value'] 284 | } 285 | } 286 | 287 | # Handles uninstall package names 288 | "^(uninstall|cleanup|virustotal|update|prefix|reset|hold|unhold)\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 289 | return ScoopLocalPackages $matches['package'] 290 | } 291 | 292 | # Handles download and cat package names 293 | "^(download|cat)\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 294 | return ScoopRemotePackages $matches['package'] + ScoopLocalPackages $matches['package'] 295 | } 296 | 297 | # Handles config param names 298 | "^config rm\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 299 | return script:ScoopConfigParamsFilter $matches['param'] 300 | } 301 | 302 | # Handles install package names 303 | "^(install|info|home|depends)\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 304 | return ScoopRemotePackages $matches['package'] 305 | } 306 | 307 | # Handles cache (rm/show) cache names 308 | "^cache (rm|show)\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 309 | return ScoopLocalCaches $matches['cache'] 310 | } 311 | 312 | # Handles bucket rm bucket names 313 | "^bucket rm\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 314 | return ScoopLocalBuckets $matches['bucket'] 315 | } 316 | 317 | # Handles bucket add bucket names 318 | "^bucket add\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 319 | return ScoopRemoteBuckets $matches['bucket'] 320 | } 321 | 322 | # Handles alias rm alias names 323 | "^alias rm\s+(?:.+\s+)?(?[\w][\-\.\w]*)?$" { 324 | return ScoopAlias $matches['alias'] 325 | } 326 | 327 | # Handles Scoop help 328 | "^help (?\S*)$" { 329 | return ScoopExpandCmd $matches['cmd'] $false 330 | } 331 | 332 | # Handles Scoop 333 | "^(?$($ScoopSubcommands.Keys -join '|'))\s+(?\S*)$" { 334 | return ScoopExpandCmdParams $ScoopSubcommands $matches['cmd'] $matches['op'] 335 | } 336 | 337 | # Handles Scoop config 338 | "^config (?[\w][\-\.\w]*)\s+(?\w*)$" { 339 | return ScoopExpandConfigParamValues $matches['param'] $matches['value'] 340 | } 341 | 342 | # Handles Scoop 343 | "^(?\S*)$" { 344 | return ScoopExpandCmd $matches['cmd'] $true 345 | } 346 | 347 | # Handles Scoop -- 348 | "^(?$ScoopCommandsWithLongParams).* --(?\S*)$" { 349 | return ScoopExpandLongParams $matches['cmd'] $matches['param'] 350 | } 351 | 352 | # Handles Scoop - 353 | "^(?$ScoopCommandsWithShortParams).* -(?\S*)$" { 354 | return ScoopExpandShortParams $matches['cmd'] $matches['shortparam'] 355 | } 356 | } 357 | } 358 | 359 | # Register-ArgumentCompleter impl, should work fork Windows PowerShell 5.1 and PowerShell Core (all version) 360 | # Old TabExpansion has been removed, if you have a compatibility issue, please report. 361 | 362 | # Thanks to mklement0, https://github.com/Moeologist/scoop-completion/issues/35#issuecomment-1897498690 363 | 364 | function script:Get-AliasNames($exe) { 365 | @($exe, "$exe.ps1", "$exe.cmd") + @(Get-Alias | Where-Object { $_.Definition -eq $exe } | Select-Object -Exp Name) 366 | } 367 | 368 | Register-ArgumentCompleter -Native -CommandName (Get-AliasNames scoop) -ScriptBlock { 369 | param($wordToComplete, $commandAst, $cursorColumn) 370 | 371 | # NOTE: 372 | # * The stringified form of $commandAst is the command's own command line (irrespective of 373 | # whether other statements are on the same line or whether it is part of a pipeline). 374 | # * The command name itself - assumed to contain no spaces - is removed, so that only the 375 | # list of arguments is passed to ScoopTabExpansion. 376 | # * However, trailing whitespace is trimmed in the string representation of $commandAst. 377 | # Therefore, when the actual command line ends in space(s), they must be added back 378 | # so that ScoopTabExpansion recognizes the start of a new argument. 379 | # .TrimStart() ensures that if there are no arguments yet at all, the empty string is passed, 380 | # which is what ScoopTabExpansion expects. 381 | $ownCommandLine = [string] $commandAst 382 | $ownCommandLine = $ownCommandLine.Substring(0, [Math]::Min($ownCommandLine.Length, $cursorColumn)) 383 | $argList = (($ownCommandLine -replace '^\S+\s*') + ' ' * ($cursorColumn - $ownCommandLine.Length)).TrimStart() 384 | 385 | ScoopTabExpansion $argList 386 | } --------------------------------------------------------------------------------