Friday Fun: PowerShell ISE Function Finder

At the PowerShell Deep Dive in San Diego, I did a lightning session showing off something I had been working on. Sometimes I don’t know what possesses me, but I felt the need for a better way to navigate my PowerShell scripts files that had many functions. Some files, especially modules, can get quite long and contain a number of functions. When using the PowerShell ISE I wanted a faster way to jump to a function. The problem is I don’t always remember what I called a function or where it is in the file. It is very easy to jump to a particular line in the ISE using the Ctrl+G shortcut.

So I started with some basics.


$Path=$psise.CurrentFile.FullPath

I decided I’d use a regular expression pattern to find my functions. I write my functions like this:


Function Get-Foo {

Param()
...

So I came up with a regex pattern to match the first line and to include the Filter keyword as well.


[regex]$r="^(Function|Filter)\s\S+\s{"

I first thought of searching the content of the current file, but that won’t give me a line number. Then I thought of Select-String. With my regex pattern, I can get the content of the current file and pipe it to Select-String. The match object that comes out the other end of the pipeline includes a line number property (awesome) and the matching line. I decided to do a little string parsing on the later to drop off the trailing curly brace.


$list=get-content $path |
select-string $r | Select LineNumber,
@{Name="Function";Expression={$_.Line.Split()[1]}}

Because I’m in the ISE I felt the need to stay graphical, so my first thought was to pipe the results to Out-Gridview.


$list | out-gridview -Title $psise.CurrentFile.FullPath

Here’s a sample result.

Now I can see the function name and line number. In the ISE I can do Ctrl+G and jump to the function. Of course, if I modify the file and line numbers change I need to close the grid and re-run my command. But wait, there’s more….

I’ve never done much with the WPF and figured this would be a great opportunity to do something with the ShowUI module. I already had the data. All I had to do was create a form with ShowUI. This is what I ended up with.


[string]$n=$psise.CurrentFile.DisplayName
ScrollViewer -ControlName $n -CanContentScroll -tag $psise.CurrentFile.FullPath -content {
StackPanel -MinWidth 300 -MaxHeight 250 `
-orientation Vertical -Children {
#get longest number if more than one function is found
if ($count -eq 1) {
[string]$i=$list.Linenumber
}
else {
[string]$i=$list[-1].Linenumber
}
$l=$i.length
foreach ($item in $list) {

[string]$text="{0:d$l} {1}" -f $item.linenumber,$item.function

Button $text -HorizontalContentAlignment Left -On_Click {
#get the line number
[regex]$num="^\d+"
#parse out the line number
$goto = $num.match($this.content).value
#grab the file name from the tab value of the parent control
[string]$f= $parent | get-uivalue
#Open the file in the editor
psedit $f
#goto the selected line
$psise.CurrentFile.Editor.SetCaretPosition($goto,1)
#close the control
Get-ParentControl | Set-UIValue -PassThru |Close-Control
} #onclick
} #foreach
}
} -show

The result is a stack panel of buttons in a scroll control. The button shows the line number and function name.

When a button is clicked, the function gets the line number and automatically jumps to it. Originally I was leaving the control open, but this means the function is still running. And if I change the script the line numbers are off so I simply close the form after jumping to the function.

In the end, I packaged all of this as a script file that adds a menu choice. If ShowUI is available, the function will use it. Otherwise the function defaults to Out-GridView.


Function Get-ISEFunction {

[cmdletbinding()]
Param([string]$Path=$psise.CurrentFile.FullPath)

#import ShowUI if found and use it later in the functoin
if (Get-module -name ShowUI -listavailable) {
Import-Module ShowUI
$showui=$True
}
else {
Write-Verbose "Using Out-GridView"
$showui=$False
}

#define a regex to find "function | filter NAME {"
[regex]$r="^(Function|Filter)\s\S+\s{"

$list=get-content $path |
select-string $r | Select LineNumber,
@{Name="Function";Expression={$_.Line.Split()[1]}}

#were any functions found?
if ($list) {
$count=$list | measure-object | Select-object -ExpandProperty Count
Write-Verbose "Found $count functions"
if ($showui) {
<#
display function list with a WPF Form from ShowUI
Include file name so the right tab can get selected
#>

[string]$n=$psise.CurrentFile.DisplayName
Write-Verbose "Building list for $n"

ScrollViewer -ControlName $n -CanContentScroll -tag $psise.CurrentFile.FullPath -content {
StackPanel -MinWidth 300 -MaxHeight 250 `
-orientation Vertical -Children {
#get longest number if more than one function is found
if ($count -eq 1) {
[string]$i=$list.Linenumber
}
else {
[string]$i=$list[-1].Linenumber
}
$l=$i.length
foreach ($item in $list) {

[string]$text="{0:d$l} {1}" -f $item.linenumber,$item.function
Write-Verbose $text
Button $text -HorizontalContentAlignment Left -On_Click {
#get the line number
[regex]$num="^\d+"
#parse out the line number
$goto = $num.match($this.content).value
#grab the file name from the tab value of the parent control
[string]$f= $parent | get-uivalue
#Open the file in the editor
psedit $f
#goto the selected line
$psise.CurrentFile.Editor.SetCaretPosition($goto,1)
#close the control
Get-ParentControl | Set-UIValue -PassThru |Close-Control
} #onclick
} #foreach
}
} -show

} #if $showui
else {
#no ShowUI module so use Out-GridView
$list | out-gridview -Title $psise.CurrentFile.FullPath
}
}
else {
Write-Host "No functions found in $($psise.CurrentFile.FullPath)" -ForegroundColor Magenta
}

} #close function

#Add to the Add-ons menu
$PSISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("List Functions",{Get-ISEFunction},$null)

#optional alias
set-alias gif get-isefunction

Now, I can click the List Functions menu choice and I’ll get a graphical list of any functions in the current file. I’m sure the regex could be tweaked. I’m also sure there are improvements I could make to the ShowUI code, but it works.

Download Get-ISEFunction and let me know what you think.

One thought on “Friday Fun: PowerShell ISE Function Finder”

Comments are closed.