Skip to content

Commit 87afebd

Browse files
committed
Improve tab expansion to handle params for push.
Fix #234 BTW if we decide this is an OK fix (need another set of eyes on my $gitParams regex), I'd like to review the fact that all these switch regex case statements fallthrough to the next (observed while debugging tab expansion). I don't believe that is necessary. I'm thinking that most (all) of these should have a break statement. Started a minimum set of Pester tests for tab completion. Would be good to flesh these out over time. Add debug configuration for VSCode that is dedicated to debugging Pester tests.
1 parent 5ada37f commit 87afebd

File tree

4 files changed

+83
-26
lines changed

4 files changed

+83
-26
lines changed

.vscode/launch.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4+
{
5+
"type": "PowerShell",
6+
"request": "launch",
7+
"name": "PowerShell Launch Pester Tests",
8+
"script": "${workspaceRoot}/test/testDebugHarness.ps1",
9+
"args": [],
10+
"cwd": "${file}"
11+
},
412
{
513
"type": "PowerShell",
614
"request": "launch",
715
"name": "PowerShell Launch (current file)",
816
"script": "${file}",
917
"args": [],
1018
"cwd": "${file}"
19+
},
20+
{
21+
"type": "PowerShell",
22+
"request": "attach",
23+
"name": "PowerShell Attach to Host Process",
24+
"processId": "${command.PickPSHostProcess}",
25+
"runspaceId": 1
1126
}
1227
]
13-
}
28+
}

src/GitTabExpansion.ps1

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,18 @@ $gitflowsubcommands = @{
2929
}
3030

3131
function script:gitCmdOperations($commands, $command, $filter) {
32-
$commands.$command -split ' ' |
33-
Where-Object { $_ -like "$filter*" }
32+
$commands.$command -split ' ' | Where-Object { $_ -like "$filter*" }
3433
}
3534

36-
3735
$script:someCommands = @('add','am','annotate','archive','bisect','blame','branch','bundle','checkout','cherry',
3836
'cherry-pick','citool','clean','clone','commit','config','describe','diff','difftool','fetch',
3937
'format-patch','gc','grep','gui','help','init','instaweb','log','merge','mergetool','mv',
4038
'notes','prune','pull','push','rebase','reflog','remote','rerere','reset','revert','rm',
4139
'shortlog','show','stash','status','submodule','svn','tag','whatchanged', 'worktree')
4240
try {
43-
if ($null -ne (git help -a 2>&1 | Select-String flow)) {
44-
$script:someCommands += 'flow'
45-
}
41+
if ($null -ne (git help -a 2>&1 | Select-String flow)) {
42+
$script:someCommands += 'flow'
43+
}
4644
}
4745
catch {
4846
Write-Debug "Search for 'flow' in 'git help' output failed with error: $_"
@@ -52,7 +50,8 @@ function script:gitCommands($filter, $includeAliases) {
5250
$cmdList = @()
5351
if (-not $global:GitTabSettings.AllCommands) {
5452
$cmdList += $someCommands -like "$filter*"
55-
} else {
53+
}
54+
else {
5655
$cmdList += git help --all |
5756
Where-Object { $_ -match '^ \S.*' } |
5857
ForEach-Object { $_.Split(' ', [StringSplitOptions]::RemoveEmptyEntries) } |
@@ -62,30 +61,32 @@ function script:gitCommands($filter, $includeAliases) {
6261
if ($includeAliases) {
6362
$cmdList += gitAliases $filter
6463
}
64+
6565
$cmdList | Sort-Object
6666
}
6767

6868
function script:gitRemotes($filter) {
69-
git remote |
70-
Where-Object { $_ -like "$filter*" }
69+
git remote | Where-Object { $_ -like "$filter*" }
7170
}
7271

7372
function script:gitBranches($filter, $includeHEAD = $false, $prefix = '') {
7473
if ($filter -match "^(?<from>\S*\.{2,3})(?<to>.*)") {
7574
$prefix += $matches['from']
7675
$filter = $matches['to']
7776
}
78-
$branches = @(git branch --no-color | ForEach-Object { if(($_ -notmatch "^\* \(HEAD detached .+\)$") -and ($_ -match "^\*?\s*(?<ref>.*)")) { $matches['ref'] } }) +
79-
@(git branch --no-color -r | ForEach-Object { if($_ -match "^ (?<ref>\S+)(?: -> .+)?") { $matches['ref'] } }) +
77+
78+
$branches = @(git branch --no-color | ForEach-Object { if (($_ -notmatch "^\* \(HEAD detached .+\)$") -and ($_ -match "^\*?\s*(?<ref>.*)")) { $matches['ref'] } }) +
79+
@(git branch --no-color -r | ForEach-Object { if ($_ -match "^ (?<ref>\S+)(?: -> .+)?") { $matches['ref'] } }) +
8080
@(if ($includeHEAD) { 'HEAD','FETCH_HEAD','ORIG_HEAD','MERGE_HEAD' })
81+
8182
$branches |
8283
Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } |
8384
ForEach-Object { $prefix + $_ }
8485
}
8586

8687
function script:gitRemoteUniqueBranches($filter) {
8788
git branch --no-color -r |
88-
ForEach-Object { if($_ -match "^ (?<remote>[^/]+)/(?<branch>\S+)(?! -> .+)?$") { $matches['branch'] } } |
89+
ForEach-Object { if ($_ -match "^ (?<remote>[^/]+)/(?<branch>\S+)(?! -> .+)?$") { $matches['branch'] } } |
8990
Group-Object -NoElement |
9091
Where-Object { $_.Count -eq 1 } |
9192
Select-Object -ExpandProperty Name |
@@ -100,7 +101,7 @@ function script:gitTags($filter, $prefix = '') {
100101

101102
function script:gitFeatures($filter, $command){
102103
$featurePrefix = git config --local --get "gitflow.prefix.$command"
103-
$branches = @(git branch --no-color | ForEach-Object { if($_ -match "^\*?\s*$featurePrefix(?<ref>.*)") { $matches['ref'] } })
104+
$branches = @(git branch --no-color | ForEach-Object { if ($_ -match "^\*?\s*$featurePrefix(?<ref>.*)") { $matches['ref'] } })
104105
$branches |
105106
Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } |
106107
ForEach-Object { $prefix + $_ }
@@ -127,7 +128,7 @@ function script:gitTfsShelvesets($filter) {
127128
function script:gitFiles($filter, $files) {
128129
$files | Sort-Object |
129130
Where-Object { $_ -like "$filter*" } |
130-
ForEach-Object { if($_ -like '* *') { "'$_'" } else { $_ } }
131+
ForEach-Object { if ($_ -like '* *') { "'$_'" } else { $_ } }
131132
}
132133

133134
function script:gitIndex($filter) {
@@ -161,9 +162,9 @@ function script:gitDeleted($filter) {
161162

162163
function script:gitAliases($filter) {
163164
git config --get-regexp ^alias\. | ForEach-Object{
164-
if($_ -match "^alias\.(?<alias>\S+) .*") {
165+
if ($_ -match "^alias\.(?<alias>\S+) .*") {
165166
$alias = $Matches['alias']
166-
if($alias -like "$filter*") {
167+
if ($alias -like "$filter*") {
167168
$alias
168169
}
169170
}
@@ -180,12 +181,12 @@ function script:expandGitAlias($cmd, $rest) {
180181
}
181182

182183
function GitTabExpansion($lastBlock) {
183-
Invoke-Utf8ConsoleCommand {
184-
GitTabExpansionInternal $lastBlock
185-
}
184+
$res = Invoke-Utf8ConsoleCommand { GitTabExpansionInternal $lastBlock }
185+
$res
186186
}
187187

188188
function GitTabExpansionInternal($lastBlock) {
189+
$gitParams = '(?<params>\s+-(?:[aA-zZ0-9]+|-[aA-zZ0-9][aA-zZ0-9-]*)(?:=\S+)?)*'
189190

190191
if ($lastBlock -match "^$(Get-AliasPattern git) (?<cmd>\S+)(?<args> .*)$") {
191192
$lastBlock = expandGitAlias $Matches['cmd'] $Matches['args']
@@ -209,7 +210,6 @@ function GitTabExpansionInternal($lastBlock) {
209210
gitCmdOperations $subcommands $matches['cmd'] $matches['op']
210211
}
211212

212-
213213
# Handles git flow <cmd> <op>
214214
"^flow (?<cmd>$($gitflowsubcommands.Keys -join '|'))\s+(?<op>\S*)$" {
215215
gitCmdOperations $gitflowsubcommands $matches['cmd'] $matches['op']
@@ -258,22 +258,22 @@ function GitTabExpansionInternal($lastBlock) {
258258

259259
# Handles git push remote <ref>:<branch>
260260
# Handles git push remote +<ref>:<branch>
261-
"^push.* (?<remote>\S+) (?<force>\+?)(?<ref>[^\s\:]*\:)(?<branch>\S*)$" {
261+
"^push.* ${gitParams}(?<remote>\S+) (?<force>\+?)(?<ref>[^\s\:]*\:)(?<branch>\S*)$" {
262262
gitRemoteBranches $matches['remote'] $matches['ref'] $matches['branch'] -prefix $matches['force']
263263
}
264264

265265
# Handles git push remote <ref>
266266
# Handles git push remote +<ref>
267267
# Handles git pull remote <ref>
268-
"^(?:push|pull).* (?:\S+) (?<force>\+?)(?<ref>[^\s\:]*)$" {
268+
"^(?:push|pull).* ${gitParams}(?<remote>[^\s-]\S*) (?<force>\+?)(?<ref>[^\s\:]*)$" {
269269
gitBranches $matches['ref'] -prefix $matches['force']
270270
gitTags $matches['ref'] -prefix $matches['force']
271271
}
272272

273273
# Handles git pull <remote>
274274
# Handles git push <remote>
275275
# Handles git fetch <remote>
276-
"^(?:push|pull|fetch).* (?<remote>\S*)$" {
276+
"^(?:push|pull|fetch).* ${gitParams}(?<remote>\S*)$" {
277277
gitRemotes $matches['remote']
278278
}
279279

@@ -360,6 +360,10 @@ function TabExpansion($line, $lastWord) {
360360
"^$(Get-AliasPattern gitk) (.*)" { GitTabExpansion $lastBlock }
361361

362362
# Fall back on existing tab expansion
363-
default { if (Test-Path Function:\TabExpansionBackup) { TabExpansionBackup $line $lastWord } }
363+
default {
364+
if (Test-Path Function:\TabExpansionBackup) {
365+
TabExpansionBackup $line $lastWord
366+
}
367+
}
364368
}
365369
}

test/TabExpansion.Tests.ps1

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
. $PSScriptRoot\Shared.ps1
2+
3+
Describe 'TabExpansion Tests' {
4+
Context 'Fetch/Push/Pull TabExpansion Tests' {
5+
It 'Tab completes all remotes' {
6+
$result = & $module GitTabExpansionInternal 'git push '
7+
$result | Should BeExactly 'origin'
8+
}
9+
It 'Tab completes matching remotes' {
10+
$result = & $module GitTabExpansionInternal 'git push or'
11+
$result | Should BeExactly 'origin'
12+
}
13+
It 'Tab completes remote and all branches' {
14+
$result = & $module GitTabExpansionInternal 'git push origin '
15+
$result -contains 'master' | Should Be $true
16+
$result -contains 'origin/master' | Should Be $true
17+
$result -contains 'origin/HEAD' | Should Be $true
18+
}
19+
It 'Tab completes remote and matching branches' {
20+
$result = & $module GitTabExpansionInternal 'git push origin ma'
21+
$result | Should BeExactly 'master'
22+
}
23+
It 'Tab completes matching remote with preceding parameters' {
24+
$result = & $module GitTabExpansionInternal 'git push --follow-tags -u or'
25+
$result | Should BeExactly 'origin'
26+
}
27+
It 'Tab completes remote and all branches with preceding parameters' {
28+
$result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin '
29+
$result -contains 'master' | Should Be $true
30+
$result -contains 'origin/master' | Should Be $true
31+
$result -contains 'origin/HEAD' | Should Be $true
32+
}
33+
It 'Tab completes remote and matching branch with preceding parameters' {
34+
$result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ma'
35+
$result | Should BeExactly 'master'
36+
}
37+
}
38+
}

test/testDebugHarness.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11

2-
Invoke-Pester $PSScriptRoot\Utils.Tests.ps1
2+
Invoke-Pester $PSScriptRoot

0 commit comments

Comments
 (0)