/\/\o\/\/ PowerShelled

This blog has moved to http://ThePowerShellGuy.com Greetings /\/\o\/\/
$AtomFeed = ("Atom.xml")
$PreviousItems = (" PowerShell : Calendar Function (GUI) "," PowerShell : WMI Support in RC2 : Privileges and c... "," PowerShell : WMI Support in RC2 (Series part 2) "," PowerShell : WMI Support in RC2 (Series part 1) "," PowerShell RC2 and Active Directory Part 2 "," PowerShell : Hosting IronPython "," PowerShell RC2 and Active Directory "," PowerShell : Using IronPython to Connect to AD and... "," PowerShell Session video of Bruce Payette "," PowerShell : Active Directory Part 11 - moving - R... "," ")

Wednesday, October 18, 2006

 


PowerShell : Tabcompletion Part 5



I realy added a lot on my TabCompletion :


this function realy makes PowerShell fly for me :

***

*Edit* I made beter version of this script on my new
blog :
The PowerShell Guy ( http://ThePowerShellGuy.com )

As it comes with an installer its much more easy to
setup,
and it uses one database for all data, it works mostly the
same as this one

Just download the PowerTab.ZIP file from the overview page here :

http://thepowershellguy.com/blogs/posh/pages/powertab.aspx

run PowetTabSetup.Ps1 and you are ready to go

***

I will Post my current TabCompletion below, I contains a lot of additions I realy like and the GUI selecting of items in a list I think is handy also as





Some of the additions in this version might be to specific, could be constructed better or are not consistent, but this version is my own current version as it has grown I and might need some cleaning up, but I posted this version for trying out and to tweak it to your need or to get some ideas and to get you started on Custom tab completion, or just use it as is .


As I think*Note* this script is also on Codeplex in : PowerShell Utility scipts as my last version this works as is.


For this version to work you need some preparement as it needs some utility scripts and a XML file to work, the code to make the XML file is needed only once, the loading of it needs to be done every time a new shell is started, so it is best placed in your profile: (for testing if you past in all of the codeblocks your also ready to go),


Utility Functions (to be called in profile)


# Custom Tab Completion Helper Functions

Function add-tabCompletion ([string]$filter,[string]$Text,[string]$type){
$global:dsTabCompletion.Tables['CustomTabExpansion'].Rows.Add($filter,$text,$type)
}

Function Save-tabCompletion {
$global:dsTabCompletion.WriteXml("$PSHome\TabCompletion.xml")
}

Function Load-tabCompletion {
$global:dsTabCompletion = new-object data.dataset
$global:dsTabCompletion.ReadXml("$PSHome\TabCompletion.xml")
}

Function Get-tabCompletion {
$global:dsTabCompletion.tables['CustomTabExpansion']
}

Function New-tabCompletion {
$global:dsTabCompletion = new-object data.dataset

$global:dtCustomTabExpansion = new-Object data.datatable
[VOID]($global:dtCustomTabExpansion.Columns.add('Filter',[string]))
[VOID]($global:dtCustomTabExpansion.Columns.add('Text',[string]))
[VOID]($global:dtCustomTabExpansion.Columns.add('Type',[string]))
$global:dtCustomTabExpansion.tablename = 'CustomTabExpansion'
$global:dsTabCompletion.Tables.Add($global:dtCustomTabExpansion)
}

Function find-tabCompletion ([string]$filter) {
$global:dsTabCompletion.Tables['CustomTabExpansion'].select("filter like '$filter'") % {$_}
}

Function Refresh-TabCompletionAlias {
$global:dsTabCompletion.Tables['CustomTabExpansion'].select("Type = 'Alias'") % {$global:dsTabCompletion.Tables['CustomTabExpansion'].rows.remove($_)}
Get-Alias % {add-tabCompletion $_.name $_.Definition 'Alias'}
}

Function Refresh-TabCompletionFunction {
$global:dsTabCompletion.Tables['CustomTabExpansion'].select("Type = 'Function'") % {$global:dsTabCompletion.Tables['CustomTabExpansion'].rows.remove($_)}
Get-Command -commandType "Function"% {add-tabCompletion $_.name $_.name $_.CommandType}
}


Then you need to build the Tab Completion DataBase for first use.


Build XML file (one time only needed)


# Create Initial Tab-Completion DataBase

New-tabCompletion

Get-Alias % {add-tabCompletion $_.name $_.Definition 'Alias'}

Get-Command -commandType "Function, Filter, Cmdlet"% {add-tabCompletion $_.name $_.name $_.CommandType}

add-tabCompletion '%' ' foreach-object {}' 'Custom'
add-tabCompletion '?' ' Where-object {}' 'Custom'

Save-tabCompletion

Load-tabCompletion


as you can see here, after we did initial fill it we can use the functions to customize it and then save it again,.I also added 2 functions to update the Alias and Function list.


Also as te Tabcompletion function uses a GUI you need to load this help function also :


Out-DataGridView needed loaded for tabcompletion (profile)

*edit* needed formas library :

[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")


# Function out-datagridView
#
# shows piplineinput in a GUI using a datagridView
# and returns the given field on double-Click or Enter
#
# /\/\o\/\/ 2006
# http:mow001.blogspot.com

Function out-dataGridView ([String]$ReturnField){

# Make DataTable from Input

$dt = new Data.datatable
$First = $true
foreach ($item in $input){
$DR = $DT.NewRow()
$Item.PsObject.get_properties() foreach {
If ($first) {
$Col = new-object Data.DataColumn
$Col.ColumnName = $_.Name.ToString()
$DT.Columns.Add($Col) }
if ($_.value -eq $null) {
$DR.Item($_.Name) = "[empty]"
}
ElseIf ($_.IsArray) {
$DR.Item($_.Name) =[string]::Join($_.value ,";")
}
Else {
$DR.Item($_.Name) = $_.value
}
}
$DT.Rows.Add($DR)
$First = $false
}

# show Datatable in Form

$form = new-object Windows.Forms.form
$form.Size = new-object System.Drawing.Size @(1000,600)
$DG = new-object windows.forms.DataGridView
$DG.DataSource = $DT.psObject.baseobject
$DG.Dock = [System.Windows.Forms.DockStyle]::Fill
$dg.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::AutoSize
$dg.SelectionMode = 'FullRowSelect'
$dg.add_DoubleClick({
$script:ret = $this.SelectedRows % {$_.DataboundItem["$ReturnField"]}
$form.Close()
})

$form.text = "$($myinvocation.line)"
$form.KeyPreview = $true
$form.Add_KeyDown({
if ($_.KeyCode -eq 'Enter') {
$script:ret = $DG.SelectedRows % {$_.DataboundItem["$ReturnField"]}
$form.Close()
}
ElseIf ($_.KeyCode -eq 'Escape'){
$form.Close()
}
})

$form.Controls.Add($DG)
$Form.Add_Shown({$form.Activate();$dg.AutoResizeColumns()})
$script:ret = $null
[void]$form.showdialog()
$script:ret
}

And after that ofcourse load the TabCompletion replacement : TabCompletion Function (load in profile)


# TabExpansion.ps1
# RC2 Version 0.5
# Replacement of default TabExpansion function
# /\/\o\/\/ 2006
# www.ThePowerShellGuy.com


#$global:dtAssemblies = $null
#$global:dtWmiClasses = $null

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
# /\/\o\/\/ added WMI Tab completion win32_[tab]/cim_[tab]/msft_[tab]
# expansion on aliases / custom aliases
# simple () expansion
# history expansion
# GUI Expansion

param($line, $lastWord)

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

switch -regex ($line) {
# Handle property and method expansion on simple () blocks( added /\/\o\/\/)...
'^\((.+)\)\.(.*)' {
$val = "($($matches[1]))"
invoke-expression "Get-Member -inputobject $val" ? {$_.name -like "$($matches[2])*"} foreach {
if ($_.MemberType -band $_method)
{

# Return a method...
$lastword.Substring(0,$lastword.LastIndexOf('.')) + '.' + $_.name + '('
}
else {
# Return a property...
$lastword.Substring(0,$lastword.LastIndexOf('.')) + '.' + $_.name
}
}
break;
}
}

switch -regex ($lastWord) {

# GUI Shortcuts

'w_(.*)' {$global:dtWmiClasses.Select("name like 'win32_$($matches[1])%'","name") out-dataGridView name}
'A_(.*)' {$global:dtAssemblies.Select("name like '%$($matches[1])%'","name") out-dataGridView name}
'f_' {ls function: select name out-dataGridView name}
'd_' {ls select Mode,LastWriteTime,Length,Name,fullname out-dataGridView fullname}
'e_' {"@{e={};Name=}"}
'h_(.*)' {Get-History -count 900 ? {$_.CommandLine -like "$($matches[1])*"} foreach-object {$_.CommandLine}}
'g_' {Get-History -Count 100 out-dataGridView Commandline}
'c_(.*)' {$global:dsTabCompletion.Tables['CustomTabExpansion'].select("filter like '$($matches[1])%'","text") out-dataGridView text}

# WMI completion

'(win32_.*cim_.*MSFT_.*)' {
# First time fill list

if (!($global:dtWmiClasses)) {

$global:dtWmiClasses = new data.datatable
[VOID]($global:dtWmiClasses.Columns.add('name',[string]))
[VOID]($global:dtWmiClasses.Columns.add('Description',[string]))

$WmiClass = [WmiClass]''

# Set Enumeration Options

$opt = new-object system.management.EnumerationOptions
$opt.EnumerateDeep = $True
$opt.UseAmendedQualifiers = $true

$i = 0 ; write-progress "Adding WMI Classes" "$i"
$WmiClass.psBase.GetSubclasses($opt) foreach {
$i++ ; if ($i%10 -eq 0) {write-progress "Adding WMI Classes" "$i"}
[void]$global:dtWmiClasses.rows.add($_.name,($_.psbase.Qualifiers ? {$_.Name -eq 'description'} % {$_.Value}))
}
write-progress "Adding WMI Classes" "$i" -Completed
}


$global:dtWmiClasses.select("name like '$($matches[1])%'") % {$_.name}
break;
}

# Handle property and method expansion on simple () blocks( added /\/\o\/\/)...
'^\((.+)\)\.(.*)' {
$val = "($($matches[1]))"
invoke-expression "Get-Member -inputobject $val" ? {$_.name -like "$($matches[2])*"} foreach {
if ($_.MemberType -band $_method)
{

# Return a method...
$val + '.' + $_.name + '('
}
else {
# Return a property...
$val + '.' + $_.name
}
}
break;
}
# Handle property and method expansion (MultiLevel added /\/\o\/\/)...
'\$(.+)\.(.*)' {
$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
}
}
break;
}

# Remove Brackets from typename (/\/\o\/\/)
'(\[.*\])=(\w*)' {
"new-Object $($matches[1].replace('[','').replace(']',''))"
break;
}

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

# Handle methods of Types (/\/\o\/\/)..
'^(\[.*\]).(\w*)' {
invoke-expression "$($matches[1]) gm" where {$_.name -like "$($matches[2])*"} % {
if ($_.MemberType -band $_Method) {
"$($matches[1]).$($_.name)" + '('
} Else {
"$($matches[1]).$($_.name)"
}
}
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 "Adding Assembly $($_.getName().name):" "$assemblyProgress" -perc $assemblyProgress
$types = $_.GetTypes() ? {$_.IsPublic -eq $true}
$types % {$j = 0} {
$j++;
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
$global:dtAssemblies.rows.add("$_",$dc,$ns)
}

# Stubs added
$dtAssemblies.rows.add('windows stub',2,'System.Windows')
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","[IronPython"}
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)]"}
$res
}
}
break;
}








# 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
}
}
break;
}

# Handle variable name expansion (original)...
'(.*^\$)(\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;
}

# Custom additions

'(.*)' {
$global:dsTabCompletion.Tables['CustomTabExpansion'].select("filter like '$($matches[1])'") % {$_.text} # AND type = 'Alias'"
}
'(.*)%' {
$global:dsTabCompletion.Tables['CustomTabExpansion'].select("filter like '$($matches[1])%'","text") % {$_.text}
}
'(.*-.*)' {
$global:dsTabCompletion.Tables['CustomTabExpansion'].select("filter like '$($matches[1])%'","text") % {$_.text}
}

} # EO switch
}



now you are free to edit your tabcompletion
try this now



# Alias

PoSH> gwmi[tab]
PoSH> Get-WmiObject win32_sh[tab]
Posh> Get-WmiObject Win32_Share

# Function + CmdLet (on -)

PoSH> get-t[tab]
PoSH>Get-tabCompletion

# Function + CmdLet (on %)

PoSH>ref%[tab]
PoSH>Refresh-TabCompletionAlias

# GUI tabcompletion :

PoSH>w_sh[tab]
PoSH>f_[tab]
PoSH>w_ [tab]

# Custom

PoSH>ls %[tab]
PoSH>ls foreach-object {}

# () resolving

PoSH>(LS).[tab]

PoSH>(gwmi win32_share).[tab]



Enjoy,

Greetings, /\/\o\/\/

Tags : Monad PowerShell




Comments:
Anonymous Daniel Cazzulino
Hi MoW,
here goes an extension I added to your tab expansion, which automatically adds to the cache the types from any assembly loaded after the initial cache was loaded. I'm doing it by just monitoring the AppDomain.AssemblyLoad event, and copying your type caching code.

Relevant fragment is:

if (!($global:TabExpansionAssemblies)) {
#current code here... [appdomain]::CurrentDomain.add_AssemblyLoad({
$types = $_.LoadedAssembly.GetTypes()
$types | foreach {$j = 0} {
$j++;
if (($j % 500) -eq 0) {
[int]$typeProgress = ($j * 100) / $types.Length
Write-Progress "Adding types '$($_.FullName)'" "$typeProgress" -perc $typeProgress -id 1
}

$dc = $_.fullName.split(".").count - 1
$ns = $_.namespace
$global:TabExpansionAssemblies.rows.add("$_", $dc, $ns)
}

Write-Progress "Adding types" "100" -perc 100 -id 1
}
}
 
Anonymous Ian Pender
Hi Mow/Daniel,

Whenever I execute the TabExpansion replacements I see no effect. What am I doing wrong ? I realise this coudl eb beginner, but when I 'dot' it, the script runs but my Tab Expansion is the same.

Moreover if I cat function:TabExpansion, I DO NOT see the amenddd code... any ideas ?
 
Post a Comment

Links to this post:

Create a Link



<< Home

Archives

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.
Google
 
Web mow001.blogspot.com

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