/\/\o\/\/ PowerShelled

This blog has moved to http://ThePowerShellGuy.com Greetings /\/\o\/\/
$AtomFeed = ("Atom.xml")
$PreviousItems = (" The DFO Show - Introducing Windows PowerShell "," PowerShell Tab Completion Part 3 of more ? "," PowerShell Tab Completion Part 2 "," The Microsoft Code "," Powershell links and free E-book "," PowerShell Tab Completion "," PowerShell out-DataGrid update and more Dataset ut... "," PowerShell : Getting Subdirectories using WMI part... "," PowerShell : Getting Subdirectories using WMI part... "," PowerShell : Getting Subdirectories using WMI "," ")

Thursday, June 08, 2006


PowerShell Tab Completion Part 4

I made another addition to the Tab Completion handeling of PowerShell, multilevel Variable completion.

*Edit* There are newer versions of this script, you can find the
latest on my new blog :
The PowerShell Guy ( http://ThePowerShellGuy.com

This versions are much more powerfull

Just download the latest PowerTab.ZIP file from the overview page
here :
run PowetTabSetup.Ps1 and you are ready to go

I made this addition as the standard tabcompletion, does only expand methods and properties on the first level, and on hitting [tab] the what I did find very annoying.


$host.U [tab]

$host.UI.R [Tab]

$host.UI.RawUI.f [Tab]

with the new tabexpansion function :

$Host.UI.Ra [tab]
$Host.UI.RawUI. [tab]

$Host.UI.RawUI.get_BackgroundColor().Ge [tab] [tab]

PoSH>$Host.UI.RawUI.Fo [tab]

Now the following changes are made from the default tab handling :

# - Methods and properties of types

[regex]::M [tab]

# - tab completion on types.
[system.ma [tab]
[System.Management.Ma [tab] [tab]

# DBMwS: Added Progressbar and Scoped variable name expansion

$env:A [tab]

$global:dt [tab]

# - shows get_ methods

removed filter to also see get_ methods handy for WMI and AD

# - MultiLevel variable Tab Completion

PoSH>$Host.UI.RawUI.Fo [tab]

# - Bracet Removal

this one I did add for new-object use, as a shortcut so that I can use tabcompletion on the -TypeName parameter, I type a '[' complete the typename, type '=' and [tab] and the braces are removed.

new-object [s [tab]
new-object [System.Ma [tab]
new-object [System.Management.Ma [tab]
new-object [System.Management.ManagementClass]
new-object [System.Management.ManagementClass]= [tab]
new-object System.Management.ManagementClass

# show assemblyname in progressbar

just a visual change.

and here is the new function TabExpansion function :.

# 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

param($line, $lastWord)

begin {
$_Method = [Management.Automation.PSMemberTypes] 'Method,CodeMethod,ScriptMethod,ParameterizedProperty'
$_ScopeNames = @("global", "local", "script", "private")

process {
switch -regex ($lastWord)
# Handle methods of Types (/\/\o\/\/)..
'(\[.*\])::(\w*)' {
invoke-expression "$($matches[1]) gm -static" where {$_.name -like "$($matches[2])*"} % {
if ($_.MemberType -band $_Method) {
"$($matches[1])::$($_.name)" + '('
} Else {

# Remove Brackets from typename (/\/\o\/\/)
'(\[.*\])=(\w*)' {

# 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
$assemblies = [appdomain]::CurrentDomain.getassemblies()
[void] ($assemblies % {$i = 0} {
[int]$assemblyProgress = ($i * 100) / $assemblies.Length
write-progress "Adding Assembly $($_.getName().name):" "$assemblyProgress" -perc $assemblyProgress
$types = $_.GetTypes()
$types % {$j = 0} {
if (($j % 200) -eq 0) {
[int]$typeProgress = ($j * 100) / $types.Length
write-progress "Adding types percent complete :"`
"$typeProgress" -perc $typeProgress -id 1

$dc = $_.fullName.split(".").count - 1
$ns = $_.namespace
write-progress "Adding types percent complete :" "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 -uni ns % {"[$($_.ns)"};
$res += $global:dtAssemblies.select("name like '$($matched)%' and dc = $dots") % {"[$($_.name)]"}

# Handle property and method expansion (MultiLevel added /\/\o\/\/)...
'\$(\w+)\.(.*)' {
$variableName = $matches[1]
$val = '$' + $matches[1]
$level = $matches[2].split('.').count
if ($level -gt 1) {
$ofs = '.';$val = '$' + $variableName + ".$($matches[2].split('.')[0..($level -2)])"
$pat = $matches[2].split('.')[($level -1)] + '*'
# /\/\o\/\/ removed : -and $n -notmatch '^[ge]et_'
# to get get_ methods on WMI and AD objects
invoke-expression "Get-Member -inputobject $val" where {$n = $_.name; $n -like $pat } foreach {
if ($_.MemberType -band $_method)
# Return a method...
$val + '.' + $_.name + '('
else {
# Return a property...
$val + '.' + $_.name

# Handle expansions for both "Scope Variable Name" and "Type Variable Names" (DbmwS)
'(.*^\$)(\w+):(\w*)$' {
$type = $matches[2]; # function, variable, etc.. that are not scopes
$prefix = $matches[1] + $type; # $ + function
$typeName = $matches[3]; # e.g. in '$function:C', value will be 'C'

if ($_ScopeNames -contains $type) {
# Scope Variable Name Expansion
foreach ($scopeVariable in
(Get-Variable "$($typeName)*" -Scope $type Sort-Object name)) {
$prefix + ":" + $scopeVariable.Name
} else {
# Type name expansion($function:, $variable, $env: ,etc)
foreach ($t in (Get-ChildItem ($type + ":" + $typeName + '*') Sort-Object name)) {
$prefix + ":" + $t.Name

# Handle variable name expansion (original)...
'(.*^\$)(\w+)$' {
$prefix = $matches[1]
$varName = $matches[2]
foreach ($v in Get-Childitem ('variable:' + $varName + '*')) {
$prefix + $v.name

# 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 }
} # EO switch
} # EO Process

you can past al code to a PowerShell console,
or save it to a file and start it dotsourced (from your profile):

. .\Tabcompletion.ps1


Greetings /\/\o\/\/
Tags :

Anonymous Jacques
Not directly related to your latest addition that I still need to test, I just wanted to let you know that I have updated the Typename resolution by fixing a bug that prevented your version from browsing namespaces inherited from newly added assemblies.
Typically, the first time you try and resolve a type name you will initiate a scan of all currently loaded assemblies. Then if some time later you load another assembly (eg Systems.Windows.Forms) you won't be able to browse its namespace because the scan occurred before.
My update mitigates this by scanning only those assemblies that are not listed as having been scanned already:

Let's keep up with the great work! :)
Anonymous Anonymous
Pardon my extreme ignorance but now that I have that code in a file what do I do with it?

If I run it at the shell prompt nothing happens. I tried tab expanding some of your examples and none of them work.

I assume there must be some way I have to convince the system to use this version rather than the default one. Being new to the PowerShell telling me to "dot source" it in my profile doesn't really mean anything to me.
Blogger /\/\o\/\/
@ Jacques :

Cool, I will look at it when I have more time I'm at Teched now.

@ Anonymous

dotsource means running the script like this :

assuming the script is c:\tab.ps1

start it like this :

. c:\tab.ps1

the dot in front will load the script in the global scope.

otherwise the function is gone again after the script does finish.

Hope this helps

Greetings /\/\o\/\/
Anonymous Anonymous
I also thought the tab completion was a great tool, but again even with the above tip it doesn't work.

PS X:\ps> dir

Directory: Microsoft.PowerShell.Core\FileSystem::X:\ps

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 6/21/2006 7:34 PM 8397 tabcompletion.ps1

PS X:\ps> .tabcompletion.ps1
'.tabcompletion.ps1' is not recognized as a cmdlet, function, operable program,
or script file.
At line:1 char:18
+ .tabcompletion.ps1 <<<<

PS X:\ps> ..tabcompletion.ps1
'..tabcompletion.ps1' is not recognized as a cmdlet, function, operable program
, or script file.
At line:1 char:19
+ ..tabcompletion.ps1 <<<<

PS X:\ps>

Any more tips?
Blogger /\/\o\/\/
@ Anonymous

You need to give the current path to start the script

. x:\ps\tabcompletion.ps1

or relative

. .\tabcompletion.ps1

note the backslash.

Greetings /\/\o\/\/
Anonymous Anonymous
Same thing... any more ideas?

PS X:\ps> .X:\ps\tabcompletion.ps1
'.X:\ps\tabcompletion.ps1' is not recognized as a cmdlet, funct
ion, operable program, or script file.
At line:1 char:40
+ .X:\ps\tabcompletion.ps1 <<<<

PS X:\ps> ..\tabcompletion.ps1
'..\tabcompletion.ps1' is not recognized as a cmdlet, function, operable progra
m, or script file.
At line:1 char:20
+ ..\tabcompletion.ps1 <<<<

PS X:\ps> dir *.ps1

Directory: Microsoft.PowerShell.Core\FileSystem::X:\ps

Mode LastWriteTime Length Name
---- ------------- ------ ----
----- 6/21/2006 7:34 PM 8397 tabcompletion.ps1
Blogger /\/\o\/\/
are you using a space between the dot and the path ?

it should be :

[dot] [space] [path]

see examples.

if that does not work are you on the RC1 bits ?
(old extension was .MSH)

Major Minor Build Revision
----- ----- ----- --------
1 0 9567 1

Hope this helps.

Greetings /\/\o\/\/
Anonymous Anonymous
That was the trick... I guess it was just hard to see the space.
Post a Comment

Links to this post:

Create a Link

<< Home


October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   April 2006   May 2006   June 2006   July 2006   August 2006   September 2006   October 2006   November 2006   December 2006  

$Links = ("PowerShell RC1 Docs"," PowerShell RC1 X86"," PowerShell RC1 X64"," Monad GettingStarted guide"," Monad Progamming Guide"," Monad SDK"," Monad videos on Channel 9"," MSH Community Workspace"," scripts.readify.net "," MonadSource"," www.reskit.net"," PowerShell Blog"," Under The Stairs"," computerperformance powershell Home"," proudlyserving"," MSH on wikipedia"," MSHWiki Channel 9"," Keith Hill's Blog"," Precision Computing"," PowerShell for fun"," MSH Memo (Japanese)"," monadblog")

find-blog -about "PowerShell","Monad" | out-Technorati.
find-blog -contains "","" | out-Technorati.
Web mow001.blogspot.com

This page is powered by Blogger. Isn't yours?