PowerShell Cookbook

Search

Categories

 

On this page

Adding Double-Tap Tab Completion to PowerShell

Archive

Blogroll

Disclaimer
I work for Microsoft.

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 218
This Year: 18
This Month: 0
This Week: 0
Comments: 529

Sign In

 Thursday, April 12, 2007
Friday, April 13, 2007 5:51:37 AM (Pacific Daylight Time, UTC-07:00) ( )

One of the gestures that becomes very ingrained when working in a Unix shell is double-tap tab completion. When you press tab at a normal slow speed, the shell cycles through available tab completion possibilities. When you press tab quickly twice in a row, the shell displays all possible completions for your command all at once – and then lets you cycle through those possibilities.

That's not a feature that PowerShell directly supports, but it is possible to get a good approximation to it by customizing your own TabExpansion function. The following script demonstrates a TabExpansion function that illustrates a framework for this. It is terrible as a stand-alone tab completion function (since it only tab completes on filenames – and poorly, at that,) but demonstrates one approach to getting double-tap tab completion in PowerShell.

It comes it two parts:

1) A TabExpansion function, that populates the area just above your prompt with suggestions if you press 'Tab' twice quickly.

function TabExpansion([string] $line, [string] $lastword)
{
    ## Delay for a bit to see if they've pressed a key again
    Start-Sleep -m 200
    if($host.UI.RawUI.KeyAvailable)
    {
        ## Get the list of items to be returned in tab completion. This
        ## is just an example.
        $items = Get-ChildItem $lastWord

        ## Convert those items into a wide string format so that we can display
        ## it concisely
        $content = $items | Format-Wide | Out-String
        $contentLines = $content.Replace("`r","").Split("`n")
        $contentHeight = $contentLines.Length + 2
        $contentWidth = $host.UI.RawUI.BufferSize.Width

        ## If there are more than 100 items (the default in
        ## Unix shells,) it would be courteous to prompt the user to find out if
        ## they actually want to display all of the tab completion information.
        if($contentLines.Length -gt 100)
        {
            return
        }

        ## Create a buffer cell array to hold the string content we plan to
        ## put in the console buffer
        $savedCells = 
        $replacementCells = $host.UI.RawUI.NewBufferCellArray(
            $contentLines,$host.UI.RawUI.ForegroundColor,$host.UI.RawUI.BackgroundColor
            )

        ## Figure out where to put the new bits of buffer
        $coordinates = $host.UI.RawUI.CursorPosition
        $coordinates.Y -= $contentHeight
        $coordinates.X = 0

        ## Bail if we can't fit the replacement content above our prompt
        if($coordinates.Y -le 0)
        {
            return
        }

        ## Create the rectangle that determines which of the old buffer cells
        ## we want to save
        $rectangle = New-Object System.Management.Automation.Host.Rectangle
        $rectangle.Left = 0
        $rectangle.Right = $contentWidth
        $rectangle.Top = $coordinates.Y
        $rectangle.Bottom = $coordinates.Y + $contentHeight

        ## Save the old buffer contents (and their coordinates) into a global
        ## variable so that the next prompt can fix it
        ${GLOBAL:Lee.Holmes.SavedBufferContents} = $host.UI.RawUI.GetBufferContents($rectangle)
        ${GLOBAL:Lee.Holmes.SavedBufferCoordinates} = $coordinates

        ## Put our double-tap tab completion information in an area above the prompt
        $host.UI.RawUI.SetBufferContents($coordinates, $replacementCells)
    }
}

2) A few additional lines in your prompt function to clean up any double-tap output at the next prompt.

    ## Restore tab completion content
    if(Test-Path Variable:\Lee.Holmes.SavedBufferContents)
    {
        $host.UI.RawUI.SetBufferContents(
            ${GLOBAL:Lee.Holmes.SavedBufferCoordinates}, ${GLOBAL:Lee.Holmes.SavedBufferContents}
        )
        ${GLOBAL:Lee.Holmes.SavedBufferContents} = $null
        ${GLOBAL:Lee.Holmes.SavedBufferCoordinates} = $null
    }


 

Comments [5] | | #