# This function creates a .ps1 file that will generate a hash of all commands and their associated options. function CreateSvnCompletion { # First get all commands with aliases by parsing the output of svn help. $help = svn help $svncmds = $help | where { $_ -match '^\s+(\w+)(?:\s\((.*)\))?' } | ` foreach { $matches[1]; if ($matches[2]) { $matches[2].Split(", ", [System.StringSplitOptions]::RemoveEmptyEntries) } } # now build a hash of all their options by parsing the output of svn help for that command $svncmdoptions = @{} foreach( $cmd in $svncmds ) { $svncmdoptions[$cmd] = svn help $cmd | where { $_ -match '^\s+(-+\w[\w\-]*)(\s+\[(.*?)\])?' } |` foreach { $matches[1]; # More than one option for the same (long option)? if ($matches[3]) { $matches[3] } } } # Now output a string that will recreate the hash '$svncmdhash=@{``' $svncmds | foreach { $opts = ($svncmdoptions[$_] | foreach{"`"{0}`"" -f $_}) "`"$_`"" + "=" + [System.String]::Join(",", $opts) +";``" } "}" } # this is the default TabExpansion function (can be obtained by doing "get-content function:TabCompletion") # with the SVN completion stuff added. # The begin and process blocks have been added. function TabExpansion { param($line, $lastWord) begin { # Has the completion file already been generated? $completionfile = "${env:TEMP}\svncompletion.ps1" if ( ! (Test-Path $completionfile) ) { # Nope, create it CreateSvnCompletion > $completionfile } # And dotsource it (this will bring . $completionfile } process { # Expand an SVN command? if ( $line -match "^\s*svn\s+(\w+)$" ) { $pat = $matches[1] + "*" $svncmdhash.keys | Sort-Object | where { $_ -like $pat } return } # Or an SVN option to an SVN command? elseif ( $line -match "^\s*svn\s(\w+).*(-[-\w]*)" ) { $cmd = $matches[1] $pat = $matches[2] + "*" $svncmdhash[$cmd] | Sort-Object | where { $_ -like $pat } return } # This is the default function to use for tab expansion. It handles simple # member expansion on variables, variable name expansion and parameter completion # on commands. It doesn't understand strings so strings containing ; | ( or { may # cause expansion to fail. switch -regex ($lastWord) { # Handle property and method expansion... '\$(\w+)\.(\w*)' { $method = [Management.Automation.PSMemberTypes] 'Method,CodeMethod,ScriptMethod,ParameterizedProperty' $variableName = $matches[1] $val = Get-Variable -value $variableName $pat = $matches[2] + '*' Get-Member -inputobject $val | where {$n = $_.name; $n -like $pat -and $n -notmatch '^[ge]et_'} | foreach { if ($_.MemberType -band $method) { # Return a method... '$' + $variableName + '.' + $_.name + '(' } else { # Return a property... '$' + $variableName + '.' + $_.name } } break; } # Handle variable name expansion... '(.*^\$)(\w+)$' { $prefix = $matches[1] $varName = $matches[2] foreach ($v in Get-Childitem ('variable:' + $varName + '*')) { $prefix + $v.name } break; } # Do completion on parameters... '^-([\w0-9]*)' { $pat = $matches[1] + '*' # extract the command name from the string # first split the string into statements and pipeline elements # This doesn't handle strings however. $cmdlet = [regex]::Split($line, '[|;]')[-1] # Extract the trailing unclosed block e.g. ls | foreach { cp if ($cmdlet -match '\{([^\{\}]*)$') { $cmdlet = $matches[1] } # Extract the longest unclosed parenthetical expression... if ($cmdlet -match '\(([^()]*)$') { $cmdlet = $matches[1] } # take the first space separated token of the remaining string # as the command to look up. Trim any leading or trailing spaces # so you don't get leading empty elements. $cmdlet = $cmdlet.Trim().Split()[0] # now get the info object for it... $cmdlet = @(Get-Command -type 'cmdlet,alias' $cmdlet)[0] # loop resolving aliases... while ($cmdlet.CommandType -eq 'alias') { $cmdlet = @(Get-Command -type 'cmdlet,alias' $cmdlet.Definition)[0] } # expand the parameter sets and emit the matching elements foreach ($n in $cmdlet.ParameterSets | Select-Object -expand parameters) { $n = $n.name if ($n -like $pat) { '-' + $n } } break; } } } }