Tag Archives: ISE

Friday Fun: Theme Me Up!

crayonsWhen PowerShell 3.0 came out, one of the compelling features was a re-vamped PowerShell ISE. Options in the ISE included the ability to fine-tune and customize the appearance including items like font, token colors, and screen colors.

If I recall correctly, in PowerShell 2.0, if you wanted to customize the appearance, you needed to add commands like this to your ISE profile script.

In fact, back then I came across a cool script from Thomas Lee. His script was a PowerShell version of something another person had posted on creating a VIM looking editor in the PowerShell ISE. After working with it for a bit, I made a few changes to handle some different file types and situations. This is my version of that script.

If you run the script in the ISE, it will turn into this:

ise-vimtheme

If you are still running PowerShell 2.0, you would need to use this script. Actually, you can use it in v3 and v4 as well. Or, in those versions you can also import themes. A theme is a ps1xml file that contains all the necessary appearance definitions. I went ahead and exported my VIM theme to a file. Save this file with a .ps1xml extension, like VIM_Theme.ps1xml. Then open the PowerShell ISE and go to Tools – Options. On the Colors and Fonts tab click Manage Themes. Click Import and get the ps1xml file. This will import it into the ISE. You might have to select the theme from the list and click OK to apply it.

You only have to do this once. From now on, every time you start the ISE you’ll get this theme. If you want to get rid of it, there is a Restore Defaults button on the Colors and Fonts tab. You can also create and export your own themes as well. But if you do, be sure to test it with different file types including .txt and .xml. Also test the different streams like Verbose and Warning. And if you do come up with a cool theme, I hope you’ll share it with the PowerShell community.

Create PowerShell Scripts with a Single Command

One of the drawbacks to using a PowerShell script or function is that you have to write it. For many IT Pros, especially those new to PowerShell, it can be difficult to know where to start. I think more people would write their own tools if there was an easy way to automatically write them. So here’s my solution. I wrote a function called New-PSCommand that is intended as a rapid development tool for a PowerShell advanced function. It should work in either PowerShell v2 or v3.

Here’s the function, although I’m not going to spend much time explaining how it works but rather how to use it.


Function New-PSCommand {
#comment base help goes here

[cmdletbinding()]

Param(
[Parameter(Mandatory=$True,HelpMessage="Enter the name of your new command")]
[ValidateNotNullorEmpty()]
[string]$Name,
[ValidateScript({
#test if using a hashtable or a v3 [ordered] hash table
($_ -is [hashtable]) -OR ($_ -is [System.Collections.Specialized.OrderedDictionary])
})]

[Alias("Parameters")]
[object]$NewParameters,
[switch]$ShouldProcess,
[string]$Synopsis,
[string]$Description,
[string]$BeginCode,
[string]$ProcessCode,
[string]$EndCode,
[switch]$UseISE
)

Write-Verbose "Starting $($myinvocation.mycommand)"
#add parameters
$myparams=""
$helpparams=""

Write-Verbose "Processing parameter names"

foreach ($k in $newparameters.keys) {
Write-Verbose " $k"
$paramsettings = $NewParameters.item($k)

#process any remaining elements from the hashtable value
#@{ParamName="type[]",Mandatory,ValuefromPipeline,ValuefromPipelinebyPropertyName,Position}

if ($paramsettings.count -gt 1) {
$paramtype=$paramsettings[0]
if ($paramsettings[1] -is [object]) {
$Mandatory = "Mandatory=`${0}," -f $paramsettings[1]
Write-Verbose $Mandatory
}
if ($paramsettings[2] -is [object]) {
$PipelineValue = "ValueFromPipeline=`${0}," -f $paramsettings[2]
Write-Verbose $PipelineValue
}
if ($paramsettings[3] -is [object]) {
$PipelineName = "ValueFromPipelineByPropertyName=`${0}" -f $paramsettings[3]
Write-Verbose $PipelineName
}
if ($paramsettings[4] -is [object]) {
$Position = "Position={0}," -f $paramsettings[4]
Write-Verbose $Position
}
}
else {
#the only hash key is the parameter type
$paramtype=$paramsettings
}

$item = "[Parameter({0}{1}{2}{3})]`n" -f $Position,$Mandatory,$PipelineValue,$PipelineName
$item +="[{0}]`${1}" -f $paramtype,$k
Write-Verbose "Adding $item to myparams"
$myparams+="$item, `n"
$helpparams+=".PARAMETER {0} `n`n" -f $k
#clear variables but ignore errors for those that don't exist
Clear-Variable "Position","Mandatory","PipelineValue","PipelineName","ParamSettings" -ErrorAction SilentlyContinue

} #foreach hash key

#get trailing comma and remove it
$myparams=$myparams.Remove($myparams.lastIndexOf(","))

Write-Verbose "Building text"
$text=@"
Function $name {
<#
.SYNOPSIS
$Synopsis

.DESCRIPTION
$Description

$HelpParams
.EXAMPLE
PS C:\> $Name

.NOTES
Version: 0.1
Author : $env:username

.INPUTS

.OUTPUTS

.LINK
#>

[cmdletbinding(SupportsShouldProcess=`$$ShouldProcess)]

Param (
$MyParams
)

Begin {
Write-Verbose "Starting `$(`$myinvocation.mycommand)"
$BeginCode
} #begin

Process {
$ProcessCode
} #process

End {
$EndCode
Write-Verbose "Ending `$(`$myinvocation.mycommand)"
} #end

} #end $name function

"@

if ($UseISE -and $psise) {
$newfile=$psise.CurrentPowerShellTab.Files.Add()
$newfile.Editor.InsertText($Text)
}
else {
Write $Text
}

Write-Verbose "Ending $($myinvocation.mycommand)"

} #end New-PSCommand function

The premise of this function is to take a hash table of parameter definitions and create a new PowerShell advanced function.

The hash table key is the name of your parameter and the key is its type. The only other value you need is the name for your new function. The New-PSCommand function creates a new advanced function, complete with comment-based help, and writes the text to the pipeline. That way you can either review it, pipe it to Out-File or copy it to the clipboard.


PS C:\>$paramhash=@{Name="string[]";Test="switch";Path="string"}
PS C:\> New-PSCommand -name "Set-MyScript" -Newparameters $paramhash | out-file "c:\scripts\set-myscript.ps1"

The hash table of parameters for my Set-MyScript function includes an array of strings for $Name, a string for $Path, and a switch for $Test. Here’s the output:


Function Set-MyScript {
<#
.SYNOPSIS

.DESCRIPTION

.PARAMETER Path

.PARAMETER Name

.PARAMETER Test

.EXAMPLE
PS C:\> Set-MyScript

.NOTES
Version: 0.1
Author : Jeff

.INPUTS

.OUTPUTS

.LINK
#>

[cmdletbinding(SupportsShouldProcess=$False)]

Param (
[Parameter()]
[string]$Path,
[Parameter()]
[string[]]$Name,
[Parameter()]
[switch]$Test
)

Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"

} #begin

Process {

} #process

End {

Write-Verbose "Ending $($myinvocation.mycommand)"
} #end

} #end Set-MyScript function

All you need to do is fill in the script with the code you want to run. New-PSCommand does all of the grunt work for you leaving you just the fun part. I also support an alternate hashtable setup if you want to specify some parameter attributes. Create a hash table using this format:


@{ParamName="type[]",Mandatory,ValuefromPipeline,ValuefromPipelinebyPropertyName,Position}

Here’s an example:

$h = @{Name="string[]",$True,$True,$False,0;
Path="string",$false,$false,$false,1;
Size="int",$false,$false,$true;
Recurse="switch"
}

I also let you specify commands to use in the Begin, Process and End scriptblocks. You can also define values for the help synopsis and description.

The last little bell on this tool is that if you run it in the PowerShell ISE, you can use the -UseISE switch which will open your new script in a new file in the ISE. This means you could open a new PowerShell tab in the ISE and run commands like this:


$hash = [ordered]@{
Name="string[]",$True,$True,$False,0
Path="string",$false,$false,$false,1
PixieDust="int",$false,$false,$true
NoUnicorn="switch"
}

$begin={
#initialize some variables
$arr=@()
$Love=$True
$ring=1
}

$end="write-host 'Finished' -foreground Green"
$synopsis = "Get magical user data"
$desc = @"
This command will do something really amazing and magical. All you need to do is provide
the right amount of pixie dust and shavings from a unicorn horn.

This requires PowerShell v4 and a full moon.
"@

. C:\scripts\New-PSCommand.ps1
New-PSCommand -Name Get-UserData -NewParameters $hash -BeginCode $begin -EndCode $end -Synopsis $synopsis -Description $desc -UseISE

By the way, I’m running PowerShell v3 so I can use a [ordered] hash table which I encourage you to use if you can. When executed, I get a new script in the ISE ready for me to finish.


Function Get-UserData {
<#
.SYNOPSIS
Get magical user data

.DESCRIPTION
This command will do something really amazing and magical. All you need to do is provide
the right amount of pixie dust and shavings from a unicorn horn.

This requires PowerShell v4 and a full moon.

.PARAMETER Name

.PARAMETER Path

.PARAMETER PixieDust

.PARAMETER NoUnicorn

.EXAMPLE
PS C:\> Get-UserData

.NOTES
Version: 0.1
Author : Jeff

.INPUTS

.OUTPUTS

.LINK
#>

[cmdletbinding(SupportsShouldProcess=$False)]

Param (
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$False)]
[string[]]$Name,
[Parameter(Position=1,Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)]
[string]$Path,
[Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$True)]
[int]$PixieDust,
[Parameter()]
[switch]$NoUnicorn
)

Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"

#initialize some variables
$arr=@()
$Love=$True
$ring=1

} #begin

Process {

} #process

End {
write-host 'Finished' -foreground Green
Write-Verbose "Ending $($myinvocation.mycommand)"
} #end

} #end Get-UserData function

I hope that a tool like this helps cut down on your development time. Please download New-PSCommand and let me know what you think.

Friday Fun: Save All Open PowerShell ISE Files

Here’s a little tidbit that I previously shared on Twitter and Google Plus. The PowerShell ISE obviously has a Save menu choice. But there’s no menu option to save all open files. But you can add one yourself. All of the open files are part of the $psise.CurrentPowerShellTab.Files collection. Each item has a Save() method so to save all the files all you need to do is enumerate the collection ForEach and invoke the Save() method. You could run a command like this in the command prompt of the ISE.


$psise.CurrentPowerShellTab.files | foreach {
$_.Save()
}

Where you will run into problems is with files that have not been saved yet and are still untitled. This code will throw exceptions. The solution is to simply skip untitled files.


$psise.CurrentPowerShellTab.files |
where {-Not ($_.IsUntitled)} |
foreach {
$_.Save()
}

You can save untitled files with the SaveAs() method but you have to provide a file name. I can’t find anyway to easily invoke the GUI prompt so for now I have manually save untitled files.

Now, instead of running this command every time, let’s add it to the ISE Add-Ons menu.


$sb={
$psise.CurrentPowerShellTab.files |
where {-Not ($_.IsUntitled)} |
foreach {
$_.Save()
}
}

$psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Save All Files",$sb,"Ctrl+Shift+A") | out-null

I even added a keyboard shortcut. Add these lines to your PowerShell ISE profile and you’ll always have this menu option. Or download Add-SaveAllISE.ps1 and load the script in your profile. As far as I can tell this works in both PowerShell v2 and v3.