Creating Add-ons, Plugins, and Tools for the PowerShell ISE

Fri, Apr 5, 2013 3-minute read

We frequently get questions asking if the PowerShell ISE supports a feature that it doesn’t. For example, variable watch windows, function browsers, or “find all matches in the current document”.

Or as another example, many of you are very familiar with Visual Studio and naturally wish for Visual Studio feature . That is usually different for each person :) The Visual Studio team is many times larger than the PowerShell team, and they’ve had a 15-year head start.

When preparing for PowerShell V3, we realized that the appetite for new functionality in the ISE would far outstrip our ability to create it, so we designed one of the most (until now) un-heralded features in the PowerShell ISE: the ability to create custom tool windows:

image

See the “Find All” pane on the right-hand side? That didn’t come with the ISE.

Creating a PowerShell ISE Add-In / Plugin is very straight-forward. Jason gives a great overview in his blog: http://blog.codeassassin.com/2012/01/16/create-a-powershell-v3-ise-add-on-tool/.

The design center of ISE plugins are that they are really just WPF UserControls. In addition, they implement the IAddOnToolHostObject interface.

When you call the .Add() method on either $psISE.CurrentPowerShellTab.VerticalAddOnTools or $psISE.CurrentPowerShellTab.HorizontalAddOnTools, the ISE will create your UserControl, set the HostObject property to an object that represents the ISE’s object model (almost identical to the $psISE object model variable you may be used to from within the ISE), and then add your control to the appropriate pane.

After that, the control is all yours.

You have full access to WPF, XAML, C#, and anything else you can imagine or would expect from a WPF control. For example, here is the simplistic XAML used to create the control in the ‘Find All’ window shown above:

image

And for an example of some “meaty” code – the actual search algorithm itself:

// The user had typed new text in the search box
private void SearchText_TextChanged(object sender, TextChangedEventArgs e)
{
    // Clear the previous results
    this.SearchResults.Items.Clear();

    // Break the file into its lines
    string[] lineBreakers = new string[] { "\r\n" };
    string[] fileText = HostObject.CurrentPowerShellTab.Files.SelectedFile.Editor.Text.Split(
        lineBreakers, StringSplitOptions.None);

    // Try to see if their search text represents a Regular Expression
    Regex searchRegex = null;
    try
    {
        searchRegex = new Regex(this.SearchText.Text, RegexOptions.IgnoreCase);
    }
    catch (ArgumentException)
    {
        // Ignore the ArgumentException that we get if the regular expression is
        // not valid.
    }

    // Go through all of the lines in the file
    for (int lineNumber = 0; lineNumber < fileText.Length; lineNumber++)
    {
        // See if the line matches the regex or literal text
        if (
            ((searchRegex != null) && (searchRegex.IsMatch(fileText[lineNumber]))) ||
            (fileText[lineNumber].IndexOf(this.SearchText.Text,
                StringComparison.CurrentCultureIgnoreCase) >= 0))
        {
            // If so, add it to the search results box.
            SearchResult result = new SearchResult() {
                Line = lineNumber + 1, Content = fileText[lineNumber]
            };
            this.SearchResults.Items.Add(result);
        }
    }
}

All-in-all, a very easy development experience.

Once you’ve compiled a DLL, adding it to the ISE is as easy as:

Add-Type -Path ’d:\documents\Visual Studio Projects\FindAll\bin\Release\FindAll.dll'
$psISE.CurrentPowerShellTab.VerticalAddOnTools.Add(‘Find All’, [FindAll.FindAllWindow], $true)

Want to play?

I’ve shared out the sample project (that implements the “Find All”) window here: https://www.leeholmes.com/projects/findall/FindAll.zip. If you want just the DLL, that’s here: https://www.leeholmes.com/projects/findall/FindAll.dll.

Enjoy!