PowerShell ISE Addon: Search all open script files

by Ravikanth on April 12, 2012

If you have used PowerShell ISE, you might have already noticed that you cannot search across all open script files and across open tabs. This becomes important when you are working with multiple files at the same time. So, this made me write a simple addon using WinForms. Now, before you think that you can do the same in very few lines of WPF — yes, I understand that but I am not yet comfortable with WPF in PowerShell. WinForms is always a winner when it comes to writing simple addons and it is probably the fastest way!

Now, let us look at the code:

$scriptBlock = {
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null

$form1 = New-Object System.Windows.Forms.Form
$button3 = New-Object System.Windows.Forms.Button
$dataGridView1 = New-Object System.Windows.Forms.DataGridView
$button1 = New-Object System.Windows.Forms.Button
$textBox1 = New-Object System.Windows.Forms.TextBox
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState

$handler_dataGridView1_CellDoubleClick=
{
    if ($datagridview1.SelectedRows.Count) {
        $row = $dataGridView1.SelectedRows[0]
        if ($row.Cells[0].Value) {
            $tab = $psISE.PowerShellTabs | ? { $_.DisplayName -eq $row.Cells[3].Value.ToString() }
            $psise.PowerShellTabs.SetSelectedPowerShellTab($tab)
            $file = $tab.Files | ? { $_.DisplayName -eq $row.Cells[0].Value }
            $tab.Files.SetSelectedFile($file)
            $file.Editor.SetCaretPosition($row.Cells[1].Value,1)
            $file.Editor.SelectCaretLine()
            $form1.Close()
        }
    }
}

$button1_OnClick=
{
    $dataGridView1.Rows.Clear()
    if ($textBox1.Text.Trim()) {
        $pattern = $textBox1.Text
        foreach ($tab in $psISE.PowerShellTabs) {
            foreach ($file in $tab.Files) {
                $result = $file.Editor.Text -split "`n" | Select-String -Pattern $textBox1.Text | Select Linenumber,Line
                if ($result) {
                    if ($result.Count) {
                        foreach ($res in $result) {
                            $dataGridView1.Rows.Add($file.DisplayName,$res.LineNumber,$res.Line.Trim(),$tab.DisplayName)
                        }
                    } else {
                        $dataGridView1.Rows.Add($file.DisplayName,$result.LineNumber,$result.Line.Trim(),$tab.DisplayName)
                    }
                }
            }
        }
    } else {
        [System.Windows.Forms.MessageBox]::Show("Enter something to search!")
    }
}

$OnLoadForm_StateCorrection=
{
	$form1.WindowState = $InitialFormWindowState
}

$form1.AcceptButton = $button1
$form1.CancelButton = $button3
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 227
$System_Drawing_Size.Width = 510
$form1.ClientSize = $System_Drawing_Size
$form1.ControlBox = $False
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.MaximizeBox = $False
$form1.MinimizeBox = $False
$form1.Name = "form1"
$form1.ShowIcon = $False
$form1.ShowInTaskbar = $False
$form1.Text = "Advanced ISE Search"
$form1.TopMost = $true
$form1.FormBorderStyle = 'Fixed3D'

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 287
$System_Drawing_Point.Y = 10
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 24
$System_Drawing_Size.Width = 104

$button3.DataBindings.DefaultDataSourceUpdateMode = 0
$button3.DialogResult = 2

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 410
$System_Drawing_Point.Y = 195
$button3.Location = $System_Drawing_Point
$button3.Name = "button3"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75
$button3.Size = $System_Drawing_Size
$button3.TabIndex = 5
$button3.Text = "Cancel"
$button3.UseVisualStyleBackColor = $True
$button3.add_Click($button3_OnClick)

$form1.Controls.Add($button3)

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 316
$System_Drawing_Point.Y = 195
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75

$dataGridView1.AutoSizeColumnsMode = 16
$System_Windows_Forms_DataGridViewTextBoxColumn_1 = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
$System_Windows_Forms_DataGridViewTextBoxColumn_1.HeaderText = "Script Name"
$System_Windows_Forms_DataGridViewTextBoxColumn_1.Name = ""
$System_Windows_Forms_DataGridViewTextBoxColumn_1.Width = 111

$dataGridView1.Columns.Add($System_Windows_Forms_DataGridViewTextBoxColumn_1)|Out-Null
$System_Windows_Forms_DataGridViewTextBoxColumn_2 = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
$System_Windows_Forms_DataGridViewTextBoxColumn_2.HeaderText = "Line Number"
$System_Windows_Forms_DataGridViewTextBoxColumn_2.Name = ""
$System_Windows_Forms_DataGridViewTextBoxColumn_2.Width = 111

$dataGridView1.Columns.Add($System_Windows_Forms_DataGridViewTextBoxColumn_2)|Out-Null
$System_Windows_Forms_DataGridViewTextBoxColumn_3 = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
$System_Windows_Forms_DataGridViewTextBoxColumn_3.HeaderText = "Line"
$System_Windows_Forms_DataGridViewTextBoxColumn_3.Name = ""
$System_Windows_Forms_DataGridViewTextBoxColumn_3.Width = 110

$dataGridView1.Columns.Add($System_Windows_Forms_DataGridViewTextBoxColumn_3)|Out-Null
$System_Windows_Forms_DataGridViewTextBoxColumn_4 = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
$System_Windows_Forms_DataGridViewTextBoxColumn_4.HeaderText = "Tab Name"
$System_Windows_Forms_DataGridViewTextBoxColumn_4.Name = ""
$System_Windows_Forms_DataGridViewTextBoxColumn_4.Width = 111

$dataGridView1.Columns.Add($System_Windows_Forms_DataGridViewTextBoxColumn_4)|Out-Null
$dataGridView1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 12
$System_Drawing_Point.Y = 39
$dataGridView1.Location = $System_Drawing_Point
$dataGridView1.Name = "dataGridView1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 150
$System_Drawing_Size.Width = 486
$dataGridView1.Size = $System_Drawing_Size
$dataGridView1.TabIndex = 3
$dataGridView1.SelectionMode = 'FullRowSelect'
$dataGridView1.MultiSelect = $false
$dataGridView1.add_CellDoubleClick($handler_dataGridView1_CellDoubleClick)

$form1.Controls.Add($dataGridView1)

$button1.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 198
$System_Drawing_Point.Y = 10
$button1.Location = $System_Drawing_Point
$button1.Name = "button1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75
$button1.Size = $System_Drawing_Size
$button1.TabIndex = 1
$button1.Text = "Search"
$button1.UseVisualStyleBackColor = $True
$button1.add_Click($button1_OnClick)

$form1.Controls.Add($button1)

$textBox1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 12
$System_Drawing_Point.Y = 12
$textBox1.Location = $System_Drawing_Point
$textBox1.Name = "textBox1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 160
$textBox1.Size = $System_Drawing_Size
$textBox1.TabIndex = 0
$textBox1.add_TextChanged($handler_textBox1_TextChanged)

$form1.Controls.Add($textBox1)

$InitialFormWindowState = $form1.WindowState

$form1.add_Load($OnLoadForm_StateCorrection)

$form1.ShowDialog()| Out-Null

}

$addonName = "Search _All Files"
$addonKey = "Ctrl+Alt+A"
$psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus.Add($addonName,$scriptBlock,$addonkey) | Out-Null

Yes, the only side-effect of using WinForms — actually PrimalForms Community Edition — is the amount of code it generates. The actual logic for searching across and moving to desired location in ISE is just 10+ lines of code.

Anyway, all you have to do is, copy / paste the above code snippet in your ISE profile.

When you select Add-Ons -> Search All Files, you will see a dialog box similar to the one below:

ISE-Search Addon

ISE-Search Addon

Once you search for a keyword, you can double click the desired row to change the focus to the search result in the editor. This search supports RegEx as well.

If you want to see this in action, just watch the below video:

Ravikanth

A technology enthu and a Windows PowerShell MVP working on SharePoint solutions at Dell Inc. Has deep interests in Windows Server OS & Virtualization.

More Posts - Website - Twitter - Facebook

  • Bernd Kriszio

    Good idea to use WindowsForms to create a modeless window.
    I added your function to my function an use it every day. Some improvements. I added  $textBox1.Text = $psise.CurrentFile.Editor.SelectedText and & $button1_OnClick. When something is selected and I invoke your function I immediately get all spots containing the selected text.
    I’m just checking, if it is practical to scan the not currently loaded files in the same directory as the current file too. Perhaps by adding a button to the form.

  • http://twitter.com/bernd_k Bernd Kriszio

    It fails, when two files (from different folders) are loaded. In this case $file gets an array and not a single file. In some cases (thanks to inconsistent capitalization)  using -ceq instead of -eq helps, but in others the names are truly identical.

Previous post:

Next post: