Skip to content

Commit

Permalink
Improve tab expansion to handle params for push.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rkeithhill committed Jan 31, 2017
1 parent 5ada37f commit 87afebd
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 26 deletions.
17 changes: 16 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "PowerShell",
"request": "launch",
"name": "PowerShell Launch Pester Tests",
"script": "${workspaceRoot}/test/testDebugHarness.ps1",
"args": [],
"cwd": "${file}"
},
{
"type": "PowerShell",
"request": "launch",
"name": "PowerShell Launch (current file)",
"script": "${file}",
"args": [],
"cwd": "${file}"
},
{
"type": "PowerShell",
"request": "attach",
"name": "PowerShell Attach to Host Process",
"processId": "${command.PickPSHostProcess}",
"runspaceId": 1
}
]
}
}
52 changes: 28 additions & 24 deletions src/GitTabExpansion.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,18 @@ $gitflowsubcommands = @{
}

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


$script:someCommands = @('add','am','annotate','archive','bisect','blame','branch','bundle','checkout','cherry',
'cherry-pick','citool','clean','clone','commit','config','describe','diff','difftool','fetch',
'format-patch','gc','grep','gui','help','init','instaweb','log','merge','mergetool','mv',
'notes','prune','pull','push','rebase','reflog','remote','rerere','reset','revert','rm',
'shortlog','show','stash','status','submodule','svn','tag','whatchanged', 'worktree')
try {
if ($null -ne (git help -a 2>&1 | Select-String flow)) {
$script:someCommands += 'flow'
}
if ($null -ne (git help -a 2>&1 | Select-String flow)) {
$script:someCommands += 'flow'
}
}
catch {
Write-Debug "Search for 'flow' in 'git help' output failed with error: $_"
Expand All @@ -52,7 +50,8 @@ function script:gitCommands($filter, $includeAliases) {
$cmdList = @()
if (-not $global:GitTabSettings.AllCommands) {
$cmdList += $someCommands -like "$filter*"
} else {
}
else {
$cmdList += git help --all |
Where-Object { $_ -match '^ \S.*' } |
ForEach-Object { $_.Split(' ', [StringSplitOptions]::RemoveEmptyEntries) } |
Expand All @@ -62,30 +61,32 @@ function script:gitCommands($filter, $includeAliases) {
if ($includeAliases) {
$cmdList += gitAliases $filter
}

$cmdList | Sort-Object
}

function script:gitRemotes($filter) {
git remote |
Where-Object { $_ -like "$filter*" }
git remote | Where-Object { $_ -like "$filter*" }
}

function script:gitBranches($filter, $includeHEAD = $false, $prefix = '') {
if ($filter -match "^(?<from>\S*\.{2,3})(?<to>.*)") {
$prefix += $matches['from']
$filter = $matches['to']
}
$branches = @(git branch --no-color | ForEach-Object { if(($_ -notmatch "^\* \(HEAD detached .+\)$") -and ($_ -match "^\*?\s*(?<ref>.*)")) { $matches['ref'] } }) +
@(git branch --no-color -r | ForEach-Object { if($_ -match "^ (?<ref>\S+)(?: -> .+)?") { $matches['ref'] } }) +

$branches = @(git branch --no-color | ForEach-Object { if (($_ -notmatch "^\* \(HEAD detached .+\)$") -and ($_ -match "^\*?\s*(?<ref>.*)")) { $matches['ref'] } }) +
@(git branch --no-color -r | ForEach-Object { if ($_ -match "^ (?<ref>\S+)(?: -> .+)?") { $matches['ref'] } }) +
@(if ($includeHEAD) { 'HEAD','FETCH_HEAD','ORIG_HEAD','MERGE_HEAD' })

$branches |
Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } |
ForEach-Object { $prefix + $_ }
}

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

function script:gitFeatures($filter, $command){
$featurePrefix = git config --local --get "gitflow.prefix.$command"
$branches = @(git branch --no-color | ForEach-Object { if($_ -match "^\*?\s*$featurePrefix(?<ref>.*)") { $matches['ref'] } })
$branches = @(git branch --no-color | ForEach-Object { if ($_ -match "^\*?\s*$featurePrefix(?<ref>.*)") { $matches['ref'] } })
$branches |
Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } |
ForEach-Object { $prefix + $_ }
Expand All @@ -127,7 +128,7 @@ function script:gitTfsShelvesets($filter) {
function script:gitFiles($filter, $files) {
$files | Sort-Object |
Where-Object { $_ -like "$filter*" } |
ForEach-Object { if($_ -like '* *') { "'$_'" } else { $_ } }
ForEach-Object { if ($_ -like '* *') { "'$_'" } else { $_ } }
}

function script:gitIndex($filter) {
Expand Down Expand Up @@ -161,9 +162,9 @@ function script:gitDeleted($filter) {

function script:gitAliases($filter) {
git config --get-regexp ^alias\. | ForEach-Object{
if($_ -match "^alias\.(?<alias>\S+) .*") {
if ($_ -match "^alias\.(?<alias>\S+) .*") {
$alias = $Matches['alias']
if($alias -like "$filter*") {
if ($alias -like "$filter*") {
$alias
}
}
Expand All @@ -180,12 +181,12 @@ function script:expandGitAlias($cmd, $rest) {
}

function GitTabExpansion($lastBlock) {
Invoke-Utf8ConsoleCommand {
GitTabExpansionInternal $lastBlock
}
$res = Invoke-Utf8ConsoleCommand { GitTabExpansionInternal $lastBlock }
$res
}

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

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


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

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

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

# Handles git pull <remote>
# Handles git push <remote>
# Handles git fetch <remote>
"^(?:push|pull|fetch).* (?<remote>\S*)$" {
"^(?:push|pull|fetch).* ${gitParams}(?<remote>\S*)$" {
gitRemotes $matches['remote']
}

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

# Fall back on existing tab expansion
default { if (Test-Path Function:\TabExpansionBackup) { TabExpansionBackup $line $lastWord } }
default {
if (Test-Path Function:\TabExpansionBackup) {
TabExpansionBackup $line $lastWord
}
}
}
}
38 changes: 38 additions & 0 deletions test/TabExpansion.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
. $PSScriptRoot\Shared.ps1

Describe 'TabExpansion Tests' {
Context 'Fetch/Push/Pull TabExpansion Tests' {
It 'Tab completes all remotes' {
$result = & $module GitTabExpansionInternal 'git push '
$result | Should BeExactly 'origin'
}
It 'Tab completes matching remotes' {
$result = & $module GitTabExpansionInternal 'git push or'
$result | Should BeExactly 'origin'
}
It 'Tab completes remote and all branches' {
$result = & $module GitTabExpansionInternal 'git push origin '
$result -contains 'master' | Should Be $true
$result -contains 'origin/master' | Should Be $true
$result -contains 'origin/HEAD' | Should Be $true
}
It 'Tab completes remote and matching branches' {
$result = & $module GitTabExpansionInternal 'git push origin ma'
$result | Should BeExactly 'master'
}
It 'Tab completes matching remote with preceding parameters' {
$result = & $module GitTabExpansionInternal 'git push --follow-tags -u or'
$result | Should BeExactly 'origin'
}
It 'Tab completes remote and all branches with preceding parameters' {
$result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin '
$result -contains 'master' | Should Be $true
$result -contains 'origin/master' | Should Be $true
$result -contains 'origin/HEAD' | Should Be $true
}
It 'Tab completes remote and matching branch with preceding parameters' {
$result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ma'
$result | Should BeExactly 'master'
}
}
}
2 changes: 1 addition & 1 deletion test/testDebugHarness.ps1
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

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

0 comments on commit 87afebd

Please sign in to comment.