Montag, 15. Februar 2010

2nd improvement to Add-IseMenu

This time I further extend Add-IseMenu, so that I have control about the order of the items. This function is compatible to the original and has extended capacibilities. This version is prove of concept. I'm still working on it.

BTW: Does anybody know how to embed seperators in ISE addon menu or has improvments to the code?

Thanks in advance.

code updated

function Add-IseMenu1 {            
<#
.Synopsis
Helper function to add menus to the ISE
.Description
Makes adding menus to the Windows PowerShell Integrated Scripting Environment (ISE)
easier. Add-IseMenu accepts a hashtable of menus.
Each key is the name of the menu.
Keys are automatically alphabetized, unless the
Each value can be one of three things:
- A Script Block
Selecting the menu item will run the script block
- A Hashtable
The value will be used to create a nested menu
- A Script Block with a note property of ShortcutKey
Selecting the menu item will run the script block.
The ShortcutKey will be used to assign a shortcut key to the item
.Example
Add-IseMenu -Name "Get" @{
"Process" = { Get-Process }
"Service" = { Get-Service }
"Hotfix" = {Get-Hotfix}
}
.Example
Add-IseMenu -Name "Verb" @{
Get = @{
Process = { Get-Process }
Service = { Get-Service }
Hotfix = { Get-Hotfix }
}
Import = @{
Module = { Import-Module }
}
}
.Example
Add-IseMenu -Name "Favorites" @{
"Edit Profile" = { psedit $profile } |
Add-Member NoteProperty ShortcutKey "CTRL + E" -PassThru
}
#>

param(
#The name of the menu to create
[Parameter(Mandatory=$true)]
[String]
$Name,
# The contents of the menu
[Parameter(Mandatory=$true)]
[Hashtable]$Menu,
# The root of the menu. This is used automatically by Add-IseMenu when it
# creates nested menus.
$Root,
# If PassThru is set, the menu items will be outputted to the pipeline
[switch]$PassThru,
# If Merge is set, menu items will be merged with existing menus rather than
# recreating the entire menu.
[switch]$Merge
)

Set-StrictMode -Off
if (-not $psise) { return }
if (-not $root) {
$root = $psise.CustomMenu
if (-not $root) {
$root = $psise.CurrentPowerShellTab.AddOnsMenu
}
if (-not $root) {
$root = $psise.CustomMenu
}
}
$iseMenu = $root.Submenus | Where-Object {
$_.DisplayName -eq $name
}
if (-not $iseMenu) {
$iseMenu = $root.Submenus.Add($name, $null, $null)
}
if (-not $merge) {
$iseMenu.Submenus.Clear()
}


$sorted = @{}
$menu.keys | % {
$order = ($menu[$_]).order
If ([int]$order -lt 0 ) { $order = 1000 + $order }
If ($order -eq $null) { $order = 500 }
$order = "{0,3}{1}" -f $order, $_
$sorted[$order] = $_ , ($menu[$_]).ShortcutKey, $menu[$_]
}
$sorted.GetEnumerator() |
Sort-Object Key |
ForEach-Object {
$itemname, $ShortcutKey, $value = $_.Value
switch ($value) {
{ $_ -is [Hashtable] } {
# Nested menu, recurse
$subMenu = $iseMenu.SubMenus.Add($itemName, $null, $null)
Add-IseMenu1 $itemName $_ -root $iseMenu -passThru:$passThru
}
{ $ShortcutKey } {
$scriptBlock= [ScriptBlock]::Create($_)
try {
$m = $iseMenu.Submenus.Add($itemName, $scriptBlock, $_.ShortcutKey)
}
catch
{
Write-Host "Shortcut $($_.ShortcutKey) already in use. Menu item created without shortcut"
$m = $iseMenu.Submenus.Add($itemName, $scriptBlock, $null)
}
if ($passThru) { $m }
}
default {
$scriptBlock= [ScriptBlock]::Create($_)
$m= $iseMenu.Submenus.Add($itemName, $scriptBlock, $null)
if ($passThru) { $m }
}
}
}
}



Add-IseMenu1 -Name "Get" @{
"Process" = { Get-Process }
"Service" = { Get-Service }
"Hotfix" = {Get-Hotfix}
}


Add-IseMenu1 -Name "Get2" @{
"Process" = { Get-Process } | Add-Member NoteProperty order 2 -PassThru
"Service" = { Get-Service } | Add-Member NoteProperty order 1 -PassThru
"Hotfix" = {Get-Hotfix} | Add-Member NoteProperty order 3 -PassThru
}

Sonntag, 14. Februar 2010

1st improvement to Add-IseMenu

I hope that all users of Powershell V2 did try ISEpack from the PowerShellPack.

In the ISEpack module there is a function Add-IseMenu for creating addon menus.

On of its shortcomings is that it is not tolerant in case when a short-cut is alredy in use.

Just replace
$m = $iseMenu.Submenus.Add($itemName, $scriptBlock, $_.ShortcutKey)            

with
try {            
$m = $iseMenu.Submenus.Add($itemName, $scriptBlock, $_.ShortcutKey)
}
catch
{
Write-Host "Shortcut $($_.ShortcutKey) already in use. Menu item created without shortcut"
$m = $iseMenu.Submenus.Add($itemName, $scriptBlock, $null)
}

and that know-all behaviour is past. Now in case of clashes, it just creates the menu item without the short cut and emits a message.

For the second shortcoming, no control about the order of the items, I have not yet a compatible idea.

Bernd

Which files are loaded in PowerShell ISE

Here is a small function to give you information about the state of the files you have loaded into the different tabs and editors of ISE:

function Get-ISELoadedFiles () {            
$tabcount = 0
foreach ($Tab in $psise.PowerShellTabs)
{
if ($Tab -eq $psise.CurrentPowerShellTab) { $currentTab = '*' } else { $currentTab = ' ' }
$filecount = 0
if ($tab.CanInvoke) { $tabmarker = " $tabcount "} else { $tabmarker = "($tabcount)"}
"{0} -----{1} {2} ----- {3}" -f $currentTab, $tabmarker, $Tab.DisplayName, $tab.Prompt
$Tab.Files | %{
if ($_ -eq $psIse.CurrentFile) { $currentFile = '*' } else { $currentFile = ' ' }
if ($_.IsSaved) {$filemarker = "$filecount " } else {$filemarker = "$filecount*" }
"{0} {1} {2}" -f $currentFile, $filemarker, $_.fullpath
$filecount++
}
$tabcount++
}

}



It seems to be a little difficult to find out whether a tab is local or remote and if remote to which machine it is connected. Please drop a coment, if you have hints.

Thanks

Bernd
(or on twitter @bernd_k)

Freitag, 12. Februar 2010

Ise Menu for Backgroud Jobs

And here is the working version. Thanks to hints from Shay Levi.

function Update-PSJobMenu()            
{
$jobs = Get-Job

if ($jobs)
{
$receive_Job_items = @{}
$stop_job_items = @{}
$remove_job_items = @{}
$jobs | % {
$receive_job_items[$_.name] = "Get-Job -id $([int] $_.Id) | Receive-Job "
$stop_job_items[$_.name] = "Stop-Job -id $([int] $_.Id) | Stop-Job "
$remove_job_items[$_.name] = "Remove-Job -id $([int] $_.Id) | Receive-Job ; Update-PSJobMenu "
}
}

$items = @{
"Get-Info" = { Get-Job }

"Run as Job" = {
$cmd = [scriptblock]::create($psIse.CurrentFile.Editor.Text)
Invoke-Command -computer localhost -ScriptBlock $cmd -asJob
Update-PSJobMenu

}
"Run selection as Job" = {
$cmd = [scriptblock]::Create($psIse.CurrentFile.Editor.SelectedText)
Invoke-Command -computer localhost -ScriptBlock $cmd -asJob
Update-PSJobMenu
}

"Stop-Job" = $stop_job_items
"Remove-Job" = $remove_job_items

"Receive-Job id" = $receive_job_items
"Refresh Menu" = { Update-PSJobMenu }
}

Add-IseMenu -name PSJobs $items
}


Update-PSJobMenu

Donnerstag, 11. Februar 2010

Ise Menu for Backgroud Jobs (Trial)

The following code is inspired by the planned articels from Ravikanth Chaganti about Background Jobs. If 'Run as Job' and 'Run selection as Job' would run it would be cool. But I don't see the difference to 'local background job (demo)' which is running. As in previous post get Add-IseMenu from ISEPack.

function Update-PSJobMenu()            
{
$jobs = Get-Job

if ($jobs)
{
$receive_Job_items = @{}
$stop_job_items = @{}
$remove_job_items = @{}
$jobs | % {
$receive_job_items[$_.name] = "Get-Job -id $([int] $_.Id) | Receive-Job "
$stop_job_items[$_.name] = "Stop-Job -id $([int] $_.Id) | Stop-Job "
$remove_job_items[$_.name] = "Remove-Job -id $([int] $_.Id) | Receive-Job ; Update-PSJobMenu "
}
}

$items = @{
"Get-Info" = { Get-Job }

# temporary until the next 2 are working and renamed to a common path
"local background job (demo)" = {
Invoke-Command -computer localhost -ScriptBlock {(Get-ChildItem C:\windows -Recurse).Count} -asJob
Update-PSJobMenu
}
# not working, but would be very cool
"Run as Job" = {
$cmd = $psIse.CurrentFile.Editor.Text
Invoke-Command -computer localhost -ScriptBlock {$cmd } -asJob
Update-PSJobMenu

}
# not working, but would be very cool
# gci C:\windows -rec
"Run selection as Job" = {
$cmd = $psIse.CurrentFile.Editor.SelectedText
Invoke-Command -computer localhost -ScriptBlock {$cmd } -asJob
Update-PSJobMenu
}

"Stop-Job" = $stop_job_items
"Remove-Job" = $remove_job_items

"Receive-Job id" = $receive_job_items
"Refresh Menu" = { Update-PSJobMenu }
}

Add-IseMenu -name PSJobs $items
}


Update-PSJobMenu



I tell me would what I have to do to fix this. Many thanks in advance.

Bernd

Mittwoch, 10. Februar 2010

PSRemoting & ISE (I)

If you weant to learn about PSRemoting read Ravikanth's PowerShell 2.0 remoting guide.

When you have
Enter-PSSession -ComputerName localhost

running (cf. part 3) and you have PowerShell V2 with ISE running check whether you haveISEPack installed.

Then you can try the following function which puts some remoting commands to you addon menu:

Add-IseMenu -name    Remoting  @{            
"Get-PSSessionConfiguration" = { Get-PSSessionConfiguration }

"Enter-PSSesion localhost" = { Enter-PSSession localhost }
"Enter-PSSession " = {
Enter-PSSession -ComputerName (Read-Host "Enter computerName: ")
if (! $?){
Enter-PSSession -ComputerName (Read-Host "Enter computerName: ") -Credential (Get-Credential)
}
}
"Enter Session Id 1" = { Enter-PSSession -Session (Get-PSSession -Id 1)}
"Enter Session Id 2" = { Enter-PSSession -Session (Get-PSSession -Id 2)}

"Exit-PSSession" = { Exit-PSSession }
"Get-PSSession" = { Get-PSSession }
"New-PSSession localhost" = { New-PSSession localhost }
"New-PSSession " = {
New-PSSession -ComputerName (Read-Host "Enter computerName: ")
if (! $?){
New-PSSession -ComputerName (Read-Host "Enter computerName: ") -Credential (Get-Credential)
}
}

# "background cmd test 1" = { Invoke-Command -Session (Get-PSSession -Id 1) -ScriptBlock {(Get-ChildItem C:\var -Recurse).Count} -asJob }
"background cmd local" = { Invoke-Command -computer localhost -ScriptBlock {(Get-ChildItem C:\var -Recurse).Count} -asJob }
"Get-Job" = { Get-Job }
"Get-Job 1" = { Get-Job -id 1 | Receive-Job }
"Import Session1 " = {Import-PSSession -Session (Get-PSSession -Id 1) -Prefix RS }
}


I think this is rather basic. Most of it runs in the local session.
But note the Exit-PSSession entry, which is prepared in the local session, but invoked from the remote session.

Generally while in remote session, you can't acces $psISE (or I didn't yet find a work around).
So while in remote session you can not modify the addon menu.
( For dynamic modifying of the addon menu see my module loader http://pauerschell.blogspot.com/ )

I hope that helps you play with PSRemoting using ISE.

Just extended the code. Thanks to Ravi.

Montag, 1. Februar 2010

Out-ISEFile (V 0.91)

Just posted an improved version at poshcode.

Note the filter Remove-TrailingBlanks, which uses regex to remove embedded trailing blanks.

And note the way I build back a collection from the objects running down the input pipeline.

Having back the object I can sen it to Out-String which has a width parameter (Snoopy dance).

For one thing I miss the option to pass the value 0 meaning unlimited and on the other side I'm not going to ask for the rationale of emiting trailing blanks to pad to the width. I guess this is a rudiment from the last millenium.

Finally note the -fl switch. I'm planning to implement some word wrapping, when this switch is selected.