Skip to content

Commit 1327b98

Browse files
authored
Merge pull request #395 from seamlessintegrations/master
Adds support for tab-completion of all parameters, long and short
2 parents 6e129ef + 703f328 commit 1327b98

File tree

4 files changed

+260
-5
lines changed

4 files changed

+260
-5
lines changed

src/GitParamTabExpansion.ps1

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Variable is used in GitTabExpansion.ps1
2+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
3+
$shortGitParams = @{
4+
add = 'n v f i p e u A N'
5+
bisect = ''
6+
blame = 'b L l t S p M C h c f n s e w'
7+
branch = 'd D l f m M r a v vv q t u'
8+
checkout = 'q f b B t l m p'
9+
cherry = 'v'
10+
'cherry-pick' = 'e x r m n s S X'
11+
clean = 'd f i n q e x X'
12+
clone = 'l s q v n o b u c'
13+
commit = 'a p C c z F m t s n e i o u v q S'
14+
config = 'f l z e'
15+
diff = 'p u s U z B M C D l S G O R a b w W'
16+
difftool = 'd y t x g'
17+
fetch = 'a f k p n t u q v'
18+
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'
19+
help = 'a g i m w'
20+
init = 'q'
21+
log = 'L n i E F g c c m r t'
22+
merge = 'e n s X q v S m'
23+
mergetool = 't y'
24+
mv = 'f k n v'
25+
notes = 'f m F C c n s q v'
26+
prune = 'n v'
27+
pull = 'q v e n s X r a f k u'
28+
push = 'n f u q v'
29+
rebase = 'm s X S q v n C f i p x'
30+
remote = 'v'
31+
reset = 'q p'
32+
revert = 'e m n S s X'
33+
rm = 'f n r q'
34+
shortlog = 'n s e w'
35+
stash = 'p k u a q'
36+
status = 's b u z'
37+
submodule = 'q b f n N'
38+
tag = 'a s u f d v n l m F'
39+
whatchanged = 'p'
40+
}
41+
42+
# Variable is used in GitTabExpansion.ps1
43+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
44+
$longGitParams = @{
45+
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'
46+
bisect = 'no-checkout term-old term-new'
47+
blame = 'root show-stats reverse porcelain line-porcelain incremental encoding= contents date score-debug show-name show-number show-email abbrev'
48+
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'
49+
checkout = 'quiet force ours theirs track no-track detach orphan ignore-skip-worktree-bits merge conflict= patch'
50+
'cherry-pick' = 'edit mainline no-commit signoff gpg-sign ff allow-empty allow-empty-message keep-redundant-commits strategy= strategy-option= ´continue quit abort'
51+
clean = 'force interactive dry-run quiet exclude='
52+
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='
53+
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'
54+
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'
55+
describe = 'dirty all tags contains abbrev candidates= exact-match debug long match always first-parent'
56+
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'
57+
difftool = 'dir-diff no-prompt prompt tool= tool-help no-symlinks symlinks extcmd= gui'
58+
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'
59+
gc = 'aggressive auto prune= no-prune quiet force'
60+
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'
61+
help = 'all guides info man web'
62+
init = 'quiet bare template= separate-git-dir= shared='
63+
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 '
64+
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'
65+
mergetool = 'tool= tool-help no-prompt prompt'
66+
mv = 'force dry-run verbose'
67+
notes = 'force message file reuse-message reedit-message ref ignore-missing stdin dry-run strategy= commit abort quiet verbose'
68+
prune = 'dry-run verbose expire'
69+
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'
70+
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'
71+
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'
72+
reflog = 'stale-fix expire= expire-unreachable= all updateref rewrite verbose'
73+
remote = 'verbose'
74+
reset = 'patch quiet soft mixed hard merge keep'
75+
revert = 'edit mainline no-edit no-commit gpg-sign signoff strategy= strategy-option continue quit abort'
76+
rm = 'force dry-run cached ignore-unmatch quiet'
77+
shortlog = 'numbered summary email format='
78+
show = 'pretty= format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes show-notes no-standard-notes standard-notes show-signature'
79+
stash = 'patch no-keep-index keep-index include-untracked all quiet index'
80+
status = 'short branch porcelain long untracked-files ignore-submodules ignored column no-column'
81+
submodule = 'quiet branch force cached files summary-limit remote no-fetch checkout merge rebase init name reference recursive depth'
82+
tag = 'annotate sign local-user force delete verify list sort column no-column contains points-at message file cleanup'
83+
whatchanged = 'since'
84+
}
85+
86+
# Variable is used in GitTabExpansion.ps1
87+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
88+
$gitParamValues = @{
89+
blame = @{
90+
encoding = 'utf-8 none'
91+
}
92+
branch = @{
93+
color = 'always never auto'
94+
abbrev = '7 8 9 10'
95+
}
96+
checkout = @{
97+
conflict = 'merge diff3'
98+
}
99+
'cherry-pick' = @{
100+
strategy = 'resolve recursive octopus ours subtree'
101+
}
102+
commit = @{
103+
'cleanup' = 'strip whitespace verbatim scissors default'
104+
}
105+
diff = @{
106+
unified = '0 1 2 3 4 5'
107+
'diff-algorithm' = 'default patience minimal histogram myers'
108+
color = 'always never auto'
109+
'word-diff' = 'color plain porcelain none'
110+
abbrev = '7 8 9 10'
111+
'diff-filter' = 'A C D M R T U X B *'
112+
'inter-hunk-context' = '0 1 2 3 4 5'
113+
'ignore-submodules' = 'none untracked dirty all'
114+
}
115+
difftool = @{
116+
tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff'
117+
}
118+
fetch = @{
119+
'recurse-submodules' = 'yes on-demand no'
120+
'recurse-submodules-default' = 'yes on-demand'
121+
}
122+
init = @{
123+
shared = 'false true umask group all world everybody o'
124+
}
125+
log = @{
126+
decorate = 'short full no'
127+
'no-walk' = 'sorted unsorted'
128+
pretty = 'oneline short medium full fuller email raw'
129+
format = 'oneline short medium full fuller email raw'
130+
encoding = 'UTF-8'
131+
date = 'relative local default iso rfc short raw'
132+
}
133+
merge = @{
134+
strategy = 'resolve recursive octopus ours subtree'
135+
log = '1 2 3 4 5 6 7 8 9'
136+
}
137+
mergetool = @{
138+
tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff'
139+
}
140+
notes = @{
141+
strategy = 'manual ours theirs union cat_sort_uniq'
142+
}
143+
pull = @{
144+
strategy = 'resolve recursive octopus ours subtree'
145+
'recurse-submodules' = 'yes on-demand no'
146+
'no-recurse-submodules' = 'yes on-demand no'
147+
rebase = 'false true preserve'
148+
}
149+
push = @{
150+
'recurse-submodules' = 'check on-demand'
151+
}
152+
rebase = @{
153+
strategy = 'resolve recursive octopus ours subtree'
154+
}
155+
revert = @{
156+
strategy = 'resolve recursive octopus ours subtree'
157+
}
158+
show = @{
159+
pretty = 'oneline short medium full fuller email raw'
160+
format = 'oneline short medium full fuller email raw'
161+
encoding = 'utf-8'
162+
}
163+
status = @{
164+
'untracked-files' = 'no normal all'
165+
'ignore-submodules' = 'none untracked dirty all'
166+
}
167+
}

src/GitTabExpansion.ps1

+46-5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ $script:someCommands = @('add','am','annotate','archive','bisect','blame','branc
3737
'format-patch','gc','grep','gui','help','init','instaweb','log','merge','mergetool','mv',
3838
'notes','prune','pull','push','rebase','reflog','remote','rerere','reset','revert','rm',
3939
'shortlog','show','stash','status','submodule','svn','tag','whatchanged', 'worktree')
40+
41+
$script:gitCommandsWithLongParams = $longGitParams.Keys -join '|'
42+
$script:gitCommandsWithShortParams = $shortGitParams.Keys -join '|'
43+
$script:gitCommandsWithParamValues = $gitParamValues.Keys -join '|'
44+
4045
try {
4146
if ($null -ne (git help -a 2>&1 | Select-String flow)) {
4247
$script:someCommands += 'flow'
@@ -100,7 +105,7 @@ function script:gitTags($filter, $prefix = '') {
100105
}
101106

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

188+
function script:expandLongParams($cmd, $filter) {
189+
$longGitParams[$cmd] -split ' ' |
190+
Where-Object { $_ -like "$filter*" } |
191+
Sort-Object |
192+
ForEach-Object { -join ("--", $_) }
193+
}
194+
195+
function script:expandShortParams($cmd, $filter) {
196+
$shortGitParams[$cmd] -split ' ' |
197+
Where-Object { $_ -like "$filter*" } |
198+
Sort-Object |
199+
ForEach-Object { -join ("-", $_) }
200+
}
201+
202+
function script:expandParamValues($cmd, $param, $filter) {
203+
$gitParamValues[$cmd][$param] -split ' ' |
204+
Where-Object { $_ -like "$filter*" } |
205+
Sort-Object |
206+
ForEach-Object { -join ("--", $param, "=", $_) }
207+
}
208+
183209
function GitTabExpansion($lastBlock) {
184210
$res = Invoke-Utf8ConsoleCommand { GitTabExpansionInternal $lastBlock }
185211
$res
186212
}
187213

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

191217
if ($lastBlock -match "^$(Get-AliasPattern git) (?<cmd>\S+)(?<args> .*)$") {
192218
$lastBlock = expandGitAlias $Matches['cmd'] $Matches['args']
@@ -258,22 +284,22 @@ function GitTabExpansionInternal($lastBlock) {
258284

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

265291
# Handles git push remote <ref>
266292
# Handles git push remote +<ref>
267293
# Handles git pull remote <ref>
268-
"^(?:push|pull)${gitParams}\s+(?<remote>[^\s-]\S*).*\s+(?<force>\+?)(?<ref>[^\s\:]*)$" {
294+
"^(?:push|pull)${ignoreGitParams}\s+(?<remote>[^\s-]\S*).*\s+(?<force>\+?)(?<ref>[^\s\:]*)$" {
269295
gitBranches $matches['ref'] -prefix $matches['force']
270296
gitTags $matches['ref'] -prefix $matches['force']
271297
}
272298

273299
# Handles git pull <remote>
274300
# Handles git push <remote>
275301
# Handles git fetch <remote>
276-
"^(?:push|pull|fetch)${gitParams}\s+(?<remote>\S*)$" {
302+
"^(?:push|pull|fetch)${ignoreGitParams}\s+(?<remote>\S*)$" {
277303
gitRemotes $matches['remote']
278304
}
279305

@@ -330,6 +356,21 @@ function GitTabExpansionInternal($lastBlock) {
330356
gitBranches $matches['ref'] $true
331357
gitTags $matches['ref']
332358
}
359+
360+
# Handles git <cmd> --<param>=<value>
361+
"^(?<cmd>$gitCommandsWithParamValues).* --(?<param>[^=]+)=(?<value>\S*)$" {
362+
expandParamValues $matches['cmd'] $matches['param'] $matches['value']
363+
}
364+
365+
# Handles git <cmd> --<param>
366+
"^(?<cmd>$gitCommandsWithLongParams).* --(?<param>\S*)$" {
367+
expandLongParams $matches['cmd'] $matches['param']
368+
}
369+
370+
# Handles git <cmd> -<shortparam>
371+
"^(?<cmd>$gitCommandsWithShortParams).* -(?<shortparam>\S*)$" {
372+
expandShortParams $matches['cmd'] $matches['shortparam']
373+
}
333374
}
334375
}
335376

src/posh-git.psm1

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ if ($psv.Major -lt 3 -and !$NoVersionWarn) {
1616
. $PSScriptRoot\Utils.ps1
1717
. $PSScriptRoot\GitUtils.ps1
1818
. $PSScriptRoot\GitPrompt.ps1
19+
. $PSScriptRoot\GitParamTabExpansion.ps1
1920
. $PSScriptRoot\GitTabExpansion.ps1
2021
. $PSScriptRoot\TortoiseGit.ps1
2122

test/GitParamTabExpansion.Tests.ps1

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
. $PSScriptRoot\Shared.ps1
2+
3+
Describe 'ParamsTabExpansion Tests' {
4+
Context 'Push Parameters TabExpansion Tests' {
5+
It 'Tab completes all long push parameters' {
6+
$result = & $module GitTabExpansionInternal 'git push --'
7+
$result -contains '--all' | Should Be $true
8+
$result -contains '--delete' | Should Be $true
9+
$result -contains '--dry-run' | Should Be $true
10+
$result -contains '--exec=' | Should Be $true
11+
$result -contains '--follow-tags' | Should Be $true
12+
$result -contains '--force' | Should Be $true
13+
$result -contains '--force-with-lease=' | Should Be $true
14+
$result -contains '--mirror' | Should Be $true
15+
$result -contains '--no-force-with-lease' | Should Be $true
16+
$result -contains '--no-thin' | Should Be $true
17+
$result -contains '--no-verify' | Should Be $true
18+
$result -contains '--porcelain' | Should Be $true
19+
$result -contains '--progress' | Should Be $true
20+
$result -contains '--prune' | Should Be $true
21+
$result -contains '--quiet' | Should Be $true
22+
$result -contains '--receive-pack=' | Should Be $true
23+
$result -contains '--recurse-submodules=' | Should Be $true
24+
$result -contains '--repo=' | Should Be $true
25+
$result -contains '--set-upstream' | Should Be $true
26+
$result -contains '--tags' | Should Be $true
27+
$result -contains '--thin' | Should Be $true
28+
$result -contains '--verbose' | Should Be $true
29+
$result -contains '--verify' | Should Be $true
30+
}
31+
It 'Tab completes all short push parameters' {
32+
$result = & $module GitTabExpansionInternal 'git push -'
33+
$result -contains '-f' | Should Be $true
34+
$result -contains '-n' | Should Be $true
35+
$result -contains '-q' | Should Be $true
36+
$result -contains '-u' | Should Be $true
37+
$result -contains '-v' | Should Be $true
38+
}
39+
It 'Tab completes push parameters values' {
40+
$result = & $module GitTabExpansionInternal 'git push --recurse-submodules='
41+
$result -contains '--recurse-submodules=check' | Should Be $true
42+
$result -contains '--recurse-submodules=on-demand' | Should Be $true
43+
}
44+
}
45+
}
46+

0 commit comments

Comments
 (0)