# TabExpansion.ps1
# Version 0.4
# Replacement of default TabExpansion function
# /\/\o\/\/ 2006
function TabExpansion {
# This is the default function that gets called for tab expansion.
# Edited by /\/\o\/\/ from the original to handle :
# - Cached tab completion on types (janel / mow).
# - Methods and properties of types
# - shows get_ methods
# - MultiLevel variable Tab Completion
# - Bracet Removal
# Edited by DBMwS: Added Progressbar and Scoped variable name expansion
# - MultiLevel variable Tab Completion for Scoped variables
# - Changed $_* variables to $Script:*
param($line, $lastWord)
begin {
$Script:Method = [Management.Automation.PSMemberTypes] `
'Method,CodeMethod,ScriptMethod,ParameterizedProperty'
$Script:ScopeNames = @("global", "local", "script", "private")
function MultiLevelPropertyExpansion {
param($parent, $child)
$private:explodedChild = $child.split('.')
$private:level = $explodedChild.Count
$private:value = $parent
if ($private:level -gt 1) {
$private:value += ".$($explodedChild[0..($level - 2)])"
}
$private:pattern = $private:explodedChild[($level -1)] + '*'
# /\/\o\/\/ removed : -and $n -notmatch '^[ge]et_'
# to get get_ methods on WMI and AD objects
$private:result = @()
$private:inputObject = '$' + $private:value
invoke-expression "Get-Member -InputObject $inputObject" |
Sort-Object name | where {$_.name -like $pattern } |
foreach {
$private:result += @{
MemberType = $_.MemberType;
Name = $_.Name;
Value = $private:value;
}
}
return $private:result
}
}
process {
switch -regex ($lastWord)
{
# Handle methods of Types (/\/\o\/\/).
# E.g.) [DateTime]::F[tab]
'(\[.*\])::(\w*)' {
invoke-expression "$($matches[1]) | gm -static" |
where { $n = $_.Name;
$n -like "$($matches[2])*" -and `
$n -notmatch '^([gs]et|op)_'} |
foreach {
if ($_.MemberType -band $Script:Method) {
"$($matches[1])::$($_.name)" + '('
} Else {
"$($matches[1])::$($_.name)"
}
}
break;
}
# Remove Brackets from typename (/\/\o\/\/)
'(\[.*\])=(\w*)' {
"$($matches[1].replace('[','').replace(']',''))"
break;
}
# Cache and Handle namespace and TypeNames (/\/\o\/\/) ..
'\[(.*)' {
$matched = $matches[1]
# only the first time Fill a DataTable with
# Typenames,namespaces and dotCount (level)
if (!($global:dtAssemblies)) {
$global:dtAssemblies = New-Object System.Data.Datatable
[void]($global:dtAssemblies.Columns.Add('name',[string]))
[void]($global:dtAssemblies.Columns.Add('DC',[int]))
[void]($global:dtAssemblies.Columns.Add('NS',[string]))
$assemblies = [appdomain]::CurrentDomain.GetAssemblies()
[void] ($assemblies | % {$i = 0} {
$i++;
[int]$assemblyProgress =
($i * 100) / $assemblies.Length
Write-Progress Caching "$assemblyProgress" `
-perc $assemblyProgress
$types = $_.GetTypes()
$types |
foreach {$j = 0} {
$j++;
if (($j % 500) -eq 0) {
[int]$typeProgress = ($j * 100) / $types.Length
Write-Progress "Adding types" "$typeProgress" `
-perc $typeProgress -id 1
}
$dc = $_.fullName.split(".").count - 1
$ns = $_.namespace
$global:dtAssemblies.rows.add("$_",$dc,$ns)
}
Write-Progress "Adding types" "100" -perc 100 -id 1
})
}
# actual tab completion
$dots = $matches[1].split(".").count - 1
switch ($dots) {
0 {"[System","[Microsoft"}
Default {
$res = @()
$res += $global:dtAssemblies.select(
"ns like '$($matched)%' and dc = $($dots + 1)") |
Select-Object -Unique ns |
foreach {"[$($_.ns)"};
$res += $global:dtAssemblies.select(
"name like '$($matched)%' and dc = $dots") |
foreach {"[$($_.name)]"}
$res
}
}
break;
}
# Handle property and method expansion (MultiLevel added /\/\o\/\/)...
'\$(\w+)\.(.*)' {
# parent variable name e.g.) $host.ui.-> parent is "host"
$parent = $matches[1];
# child variable name e.g.) $host.ui.rawui-> child is "ui.rawui"
$child = $matches[2]
MultiLevelPropertyExpansion $parent $child |
foreach {
$output = "$" + $_["Value"] + '.' + $_["Name"]
if ($_["MemberType"] -band $Script:Method) { $output += "(" }
return $output
}
break;
}
# Handle expansions for both "Scope Variable Name"
# and "Type Variable Names" (DbmwS)
'(.*^\$)(\w+):(\w+)(\.(.*))*$' {
# "function", "variable", "alias", "env:", etc...
$type = $matches[2];
# E.g.) '$' + 'function'
$prefix = $matches[1] + $type;
# e.g. in '$function:C', $typeName will be 'C'
$typeName = $matches[3];
# e.g. in '$global:host.ui.rawui', child will be 'ui.rawui'
$child = $matches[5];
# Scope Variable Name Expansion for $global:, $private:, $script:, $local:
if ($Script:ScopeNames -contains $type) {
if ($child) {
# Handle *multi-level* property and method expansion
MultiLevelPropertyExpansion $typeName $child |
foreach {
$output = $prefix + ":" + $_["Value"] + '.' + $_["Name"]
# Append "(" for method
if ($_["MemberType"] -band $Script:Method) { $output += '('}
return $output
}
} else { # Expand scope variable name
foreach ($scopeVariable in
(Get-Variable "$($typeName)*" -Scope $type |
Sort-Object name)) {
$prefix + ":" + $scopeVariable.Name
}
}
} else {
# Type name expansion for $function:, $variable, $env:, etc
foreach ($t in
(Get-ChildItem ($type + ":" + $typeName + '*') |
Sort-Object name)) {
$prefix + ":" + $t.Name
}
}
break;
}
###
### Default functions below this line
###
# Handle property and method expansion...
'\$(\w+)\.(\w*)' {
$variableName = $matches[1]
$val = Get-Variable -value $variableName
$pat = $matches[2] + '*'
Get-Member -inputobject $val |
where { $n = $_.name; `
$n -like $pat -and $n -notmatch '^([gs]et|op)_' } |
foreach {
if ($_.MemberType -band $Script: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 (original) ...
'^-([\w0-9]*)' {
$pat = $matches[1] + '*'
# extract the command name from the string
# first split the string into statements and pipeline elements
# This doesnt handle strings however.
$cmdlet = [regex]::Split($line, '[|;]')[-1]
# Extract the trailing unclosed block
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 dont 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;
}
} # EO switch
} # EO Process
}
Experimenting with a different format of blogs...