Skip to content

Commit

Permalink
Merge pull request #395 from seamlessintegrations/master
Browse files Browse the repository at this point in the history
Adds support for tab-completion of all parameters, long and short
  • Loading branch information
dahlbyk authored Feb 6, 2017
2 parents 6e129ef + 703f328 commit 1327b98
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 5 deletions.
167 changes: 167 additions & 0 deletions src/GitParamTabExpansion.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Variable is used in GitTabExpansion.ps1
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
$shortGitParams = @{
add = 'n v f i p e u A N'
bisect = ''
blame = 'b L l t S p M C h c f n s e w'
branch = 'd D l f m M r a v vv q t u'
checkout = 'q f b B t l m p'
cherry = 'v'
'cherry-pick' = 'e x r m n s S X'
clean = 'd f i n q e x X'
clone = 'l s q v n o b u c'
commit = 'a p C c z F m t s n e i o u v q S'
config = 'f l z e'
diff = 'p u s U z B M C D l S G O R a b w W'
difftool = 'd y t x g'
fetch = 'a f k p n t u q v'
grep = 'a i I w v h H E G P F n l L O z c p C A B W f e q'
help = 'a g i m w'
init = 'q'
log = 'L n i E F g c c m r t'
merge = 'e n s X q v S m'
mergetool = 't y'
mv = 'f k n v'
notes = 'f m F C c n s q v'
prune = 'n v'
pull = 'q v e n s X r a f k u'
push = 'n f u q v'
rebase = 'm s X S q v n C f i p x'
remote = 'v'
reset = 'q p'
revert = 'e m n S s X'
rm = 'f n r q'
shortlog = 'n s e w'
stash = 'p k u a q'
status = 's b u z'
submodule = 'q b f n N'
tag = 'a s u f d v n l m F'
whatchanged = 'p'
}

# Variable is used in GitTabExpansion.ps1
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
$longGitParams = @{
add = 'dry-run verbose force interactive patch edit update all no-ignore-removal no-all ignore-removal intent-to-add refresh ignore-errors ignore-missing'
bisect = 'no-checkout term-old term-new'
blame = 'root show-stats reverse porcelain line-porcelain incremental encoding= contents date score-debug show-name show-number show-email abbrev'
branch = 'color no-color list abbrev= no-abbrev column no-column merged no-merged contains set-upstream track no-track set-upstream-to= unset-upstream edit-description delete create-reflog force move all verbose quiet'
checkout = 'quiet force ours theirs track no-track detach orphan ignore-skip-worktree-bits merge conflict= patch'
'cherry-pick' = 'edit mainline no-commit signoff gpg-sign ff allow-empty allow-empty-message keep-redundant-commits strategy= strategy-option= ´continue quit abort'
clean = 'force interactive dry-run quiet exclude='
clone = 'local no-hardlinks shared reference quiet verbose progress no-checkout bare mirror origin branch upload-pack template= config depth single-branch no-single-branch recursive recurse-submodules separate-git-dir='
commit = 'all patch reuse-message reedit-message fixup squash reset-author short branch porcelain long null file author date message template signoff no-verify allow-empty allow-empty-message cleanup= edit no-edit ammend no-post-rewrite include only untracked-files verbose quiet dry-run status no-status gpg-sign no-gpg-sign'
config = 'replace-all add get get-all get-regexp get-urlmatch global system local file blob remove-section rename-section unset unset-all list bool int bool-or-int path null get-colorbool get-color edit includes no-includes'
describe = 'dirty all tags contains abbrev candidates= exact-match debug long match always first-parent'
diff = 'cached patch no-patch unified= raw patch-with-raw minimal patience histogram diff-algorithm= stat numstat shortstat dirstat summary patch-with-stat name-only name-status submodule color no-color word-diff word-diff-regex color-words no-renames check full-index binary apprev break-rewrites find-renames find-copies find-copies-harder irreversible-delete diff-filter= pickaxe-all pickaxe-regex relative text ignore-space-at-eol ignore-space-change ignore-all-space ignore-blank-lines inter-hunk-context= function-context exit-code quiet ext-diff no-ext-diff textconv no-textconv ignore-submodules src-prefix dst-prefix no-prefix'
difftool = 'dir-diff no-prompt prompt tool= tool-help no-symlinks symlinks extcmd= gui'
fetch = 'all append depth= unshallow update-shallow dry-run force keep multiple prune no-tags tags recurse-submodules= no-recurse-submodules submodule-prefix= recurse-submodules-default= update-head-ok upload-pack quiet verbose progress'
gc = 'aggressive auto prune= no-prune quiet force'
grep = 'cached no-index untracked no-exclude-standard exclude-standard text textconv no-textconv ignore-case max-depth word-regexp invert-match full-name extended-regexp basic-regexp perl-regexp fixed-strings line-number files-with-matches open-file-in-pager null count color no-color break heading show-function context after-context before-context function-context and or not all-match quiet'
help = 'all guides info man web'
init = 'quiet bare template= separate-git-dir= shared='
log = 'follow no-decorate decorate source use-mailmap full-diff log-size max-count skip since after until before author committer grep-reflog grep all-match regexp-ignore-case basic-regexp extended-regexp fixed-strings perl-regexp remove-empty merges no-merges min-parents max-parents no-min-parents no-max-parents first-parent not all branches tags remote glob= exclude= ignore-missing bisect stdin cherry-mark cherry-pick left-only right-only cherry walk-reflogs merge boundary simplify-by-decoration full-history dense sparse simplify-merges ancestry-path date-order author-date-order topo-order reverse objects objects-edge unpacked no-walk= do-walk pretty format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes standard-notes no-standard-notes show-signature relative-date date= parents children left-right graph show-linear-break '
merge = 'commit no-commit edit no-edit ff no-ff ff-only log no-log stat no-stat squash no-squash strategy strategy-option verify-signatures no-verify-signatures summary no-summary quiet verbose progress no-progress gpg-sign rerere-autoupdate no-rerere-autoupdate abort'
mergetool = 'tool= tool-help no-prompt prompt'
mv = 'force dry-run verbose'
notes = 'force message file reuse-message reedit-message ref ignore-missing stdin dry-run strategy= commit abort quiet verbose'
prune = 'dry-run verbose expire'
pull = 'quiet verbose recurse-submodules= no-recurse-submodules= commit no-commit edit no-edit ff no-ff ff-only log no-log stat no-stat squash no-squash strategy= strategy-option= verify-signatures no-verify-signatures summary no-summary rebase= no-rebase all append depth= unshallow update-shallow force keep no-tags update-head-ok upload-pack progress'
push = 'all prune mirror dry-run porcelain delete tags follow-tags receive-pack= exec= force-with-lease= no-force-with-lease force repo= set-upstream thin no-thin quiet verbose progress recurse-submodules= verify no-verify'
rebase = 'onto continue abort keep-empty skip edit-todo merge strategy= strategy-option= gpg-sign quiet verbose stat no-stat no-verify verify force-rebase fork-point no-fork-point ignore-whitespace whitespace= committer-date-is-author-date ignore-date interactive preserve-merges exec root autosquash no-autosquash autostash no-autostash no-ff'
reflog = 'stale-fix expire= expire-unreachable= all updateref rewrite verbose'
remote = 'verbose'
reset = 'patch quiet soft mixed hard merge keep'
revert = 'edit mainline no-edit no-commit gpg-sign signoff strategy= strategy-option continue quit abort'
rm = 'force dry-run cached ignore-unmatch quiet'
shortlog = 'numbered summary email format='
show = 'pretty= format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes show-notes no-standard-notes standard-notes show-signature'
stash = 'patch no-keep-index keep-index include-untracked all quiet index'
status = 'short branch porcelain long untracked-files ignore-submodules ignored column no-column'
submodule = 'quiet branch force cached files summary-limit remote no-fetch checkout merge rebase init name reference recursive depth'
tag = 'annotate sign local-user force delete verify list sort column no-column contains points-at message file cleanup'
whatchanged = 'since'
}

# Variable is used in GitTabExpansion.ps1
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
$gitParamValues = @{
blame = @{
encoding = 'utf-8 none'
}
branch = @{
color = 'always never auto'
abbrev = '7 8 9 10'
}
checkout = @{
conflict = 'merge diff3'
}
'cherry-pick' = @{
strategy = 'resolve recursive octopus ours subtree'
}
commit = @{
'cleanup' = 'strip whitespace verbatim scissors default'
}
diff = @{
unified = '0 1 2 3 4 5'
'diff-algorithm' = 'default patience minimal histogram myers'
color = 'always never auto'
'word-diff' = 'color plain porcelain none'
abbrev = '7 8 9 10'
'diff-filter' = 'A C D M R T U X B *'
'inter-hunk-context' = '0 1 2 3 4 5'
'ignore-submodules' = 'none untracked dirty all'
}
difftool = @{
tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff'
}
fetch = @{
'recurse-submodules' = 'yes on-demand no'
'recurse-submodules-default' = 'yes on-demand'
}
init = @{
shared = 'false true umask group all world everybody o'
}
log = @{
decorate = 'short full no'
'no-walk' = 'sorted unsorted'
pretty = 'oneline short medium full fuller email raw'
format = 'oneline short medium full fuller email raw'
encoding = 'UTF-8'
date = 'relative local default iso rfc short raw'
}
merge = @{
strategy = 'resolve recursive octopus ours subtree'
log = '1 2 3 4 5 6 7 8 9'
}
mergetool = @{
tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff'
}
notes = @{
strategy = 'manual ours theirs union cat_sort_uniq'
}
pull = @{
strategy = 'resolve recursive octopus ours subtree'
'recurse-submodules' = 'yes on-demand no'
'no-recurse-submodules' = 'yes on-demand no'
rebase = 'false true preserve'
}
push = @{
'recurse-submodules' = 'check on-demand'
}
rebase = @{
strategy = 'resolve recursive octopus ours subtree'
}
revert = @{
strategy = 'resolve recursive octopus ours subtree'
}
show = @{
pretty = 'oneline short medium full fuller email raw'
format = 'oneline short medium full fuller email raw'
encoding = 'utf-8'
}
status = @{
'untracked-files' = 'no normal all'
'ignore-submodules' = 'none untracked dirty all'
}
}
51 changes: 46 additions & 5 deletions src/GitTabExpansion.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ $script:someCommands = @('add','am','annotate','archive','bisect','blame','branc
'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')

$script:gitCommandsWithLongParams = $longGitParams.Keys -join '|'
$script:gitCommandsWithShortParams = $shortGitParams.Keys -join '|'
$script:gitCommandsWithParamValues = $gitParamValues.Keys -join '|'

try {
if ($null -ne (git help -a 2>&1 | Select-String flow)) {
$script:someCommands += 'flow'
Expand Down Expand Up @@ -100,7 +105,7 @@ function script:gitTags($filter, $prefix = '') {
}

function script:gitFeatures($filter, $command){
$featurePrefix = git config --local --get "gitflow.prefix.$command"
$featurePrefix = git config --local --get "gitflow.prefix.$command"
$branches = @(git branch --no-color | ForEach-Object { if ($_ -match "^\*?\s*$featurePrefix(?<ref>.*)") { $matches['ref'] } })
$branches |
Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } |
Expand Down Expand Up @@ -180,13 +185,34 @@ function script:expandGitAlias($cmd, $rest) {
}
}

function script:expandLongParams($cmd, $filter) {
$longGitParams[$cmd] -split ' ' |
Where-Object { $_ -like "$filter*" } |
Sort-Object |
ForEach-Object { -join ("--", $_) }
}

function script:expandShortParams($cmd, $filter) {
$shortGitParams[$cmd] -split ' ' |
Where-Object { $_ -like "$filter*" } |
Sort-Object |
ForEach-Object { -join ("-", $_) }
}

function script:expandParamValues($cmd, $param, $filter) {
$gitParamValues[$cmd][$param] -split ' ' |
Where-Object { $_ -like "$filter*" } |
Sort-Object |
ForEach-Object { -join ("--", $param, "=", $_) }
}

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

function GitTabExpansionInternal($lastBlock) {
$gitParams = '(?<params>\s+-(?:[aA-zZ0-9]+|-[aA-zZ0-9][aA-zZ0-9-]*)(?:=\S+)?)*'
$ignoreGitParams = '(?<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 Down Expand Up @@ -258,22 +284,22 @@ function GitTabExpansionInternal($lastBlock) {

# Handles git push remote <ref>:<branch>
# Handles git push remote +<ref>:<branch>
"^push${gitParams}\s+(?<remote>[^\s-]\S*).*\s+(?<force>\+?)(?<ref>[^\s\:]*\:)(?<branch>\S*)$" {
"^push${ignoreGitParams}\s+(?<remote>[^\s-]\S*).*\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)${gitParams}\s+(?<remote>[^\s-]\S*).*\s+(?<force>\+?)(?<ref>[^\s\:]*)$" {
"^(?:push|pull)${ignoreGitParams}\s+(?<remote>[^\s-]\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)${gitParams}\s+(?<remote>\S*)$" {
"^(?:push|pull|fetch)${ignoreGitParams}\s+(?<remote>\S*)$" {
gitRemotes $matches['remote']
}

Expand Down Expand Up @@ -330,6 +356,21 @@ function GitTabExpansionInternal($lastBlock) {
gitBranches $matches['ref'] $true
gitTags $matches['ref']
}

# Handles git <cmd> --<param>=<value>
"^(?<cmd>$gitCommandsWithParamValues).* --(?<param>[^=]+)=(?<value>\S*)$" {
expandParamValues $matches['cmd'] $matches['param'] $matches['value']
}

# Handles git <cmd> --<param>
"^(?<cmd>$gitCommandsWithLongParams).* --(?<param>\S*)$" {
expandLongParams $matches['cmd'] $matches['param']
}

# Handles git <cmd> -<shortparam>
"^(?<cmd>$gitCommandsWithShortParams).* -(?<shortparam>\S*)$" {
expandShortParams $matches['cmd'] $matches['shortparam']
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/posh-git.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ if ($psv.Major -lt 3 -and !$NoVersionWarn) {
. $PSScriptRoot\Utils.ps1
. $PSScriptRoot\GitUtils.ps1
. $PSScriptRoot\GitPrompt.ps1
. $PSScriptRoot\GitParamTabExpansion.ps1
. $PSScriptRoot\GitTabExpansion.ps1
. $PSScriptRoot\TortoiseGit.ps1

Expand Down
46 changes: 46 additions & 0 deletions test/GitParamTabExpansion.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
. $PSScriptRoot\Shared.ps1

Describe 'ParamsTabExpansion Tests' {
Context 'Push Parameters TabExpansion Tests' {
It 'Tab completes all long push parameters' {
$result = & $module GitTabExpansionInternal 'git push --'
$result -contains '--all' | Should Be $true
$result -contains '--delete' | Should Be $true
$result -contains '--dry-run' | Should Be $true
$result -contains '--exec=' | Should Be $true
$result -contains '--follow-tags' | Should Be $true
$result -contains '--force' | Should Be $true
$result -contains '--force-with-lease=' | Should Be $true
$result -contains '--mirror' | Should Be $true
$result -contains '--no-force-with-lease' | Should Be $true
$result -contains '--no-thin' | Should Be $true
$result -contains '--no-verify' | Should Be $true
$result -contains '--porcelain' | Should Be $true
$result -contains '--progress' | Should Be $true
$result -contains '--prune' | Should Be $true
$result -contains '--quiet' | Should Be $true
$result -contains '--receive-pack=' | Should Be $true
$result -contains '--recurse-submodules=' | Should Be $true
$result -contains '--repo=' | Should Be $true
$result -contains '--set-upstream' | Should Be $true
$result -contains '--tags' | Should Be $true
$result -contains '--thin' | Should Be $true
$result -contains '--verbose' | Should Be $true
$result -contains '--verify' | Should Be $true
}
It 'Tab completes all short push parameters' {
$result = & $module GitTabExpansionInternal 'git push -'
$result -contains '-f' | Should Be $true
$result -contains '-n' | Should Be $true
$result -contains '-q' | Should Be $true
$result -contains '-u' | Should Be $true
$result -contains '-v' | Should Be $true
}
It 'Tab completes push parameters values' {
$result = & $module GitTabExpansionInternal 'git push --recurse-submodules='
$result -contains '--recurse-submodules=check' | Should Be $true
$result -contains '--recurse-submodules=on-demand' | Should Be $true
}
}
}

0 comments on commit 1327b98

Please sign in to comment.