PowerShell Cookbook

Search

Categories

 

On this page

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] | | # 
Friday, April 13, 2007 7:21:45 AM (Pacific Daylight Time, UTC-07:00)
Good effort.

Although I never noticed this time-sensitive double tab tap in any shells I've used... certainly doesn't do it with my current configurations of tcsh, zsh, bash, ksh. My zsh (preferred shell) shows both, the whole lot below the current line and cycles through in place.

Nice to see the mandlebrot in colour by the way - prettier than mine at http://ps1.soapyfrog.com/2007/01/01/powershell-is-slow/
Monday, April 16, 2007 7:57:54 PM (Pacific Daylight Time, UTC-07:00)
Where'd you get the color Mandelbrot script? I'd love to get a copy of it. I googled around and didn't find anything but the text only single-line mandelbrot generator.
Math Geek
Tuesday, April 17, 2007 7:56:35 PM (Pacific Daylight Time, UTC-07:00)
Nevermind, I just modified the single-line generator to use color instead of characters. I suppose it was time for me to actually learn to write/modify powershell script instead of just playing with already completed scripts. Turns out the mod wasn't complicated at all. Now I gotta add in zooming.
Math Geek
Monday, April 23, 2007 1:21:24 PM (Pacific Daylight Time, UTC-07:00)
Hey - I apologize for the off-topic comment, but are you aware of a way to change the powershell personal profile directory? We use roaming profiles at my office and it's often much slower than using a local profile script.

Thanks for any help.
Monday, April 23, 2007 2:04:25 PM (Pacific Daylight Time, UTC-07:00)
Hi Ryan;

The other alternative is the machine-wide profiles: http://www.leeholmes.com/blog/TheStoryBehindTheNamingAndLocationOfPowerShellProfiles.aspx

Lee
Name
E-mail
Home page

Comment (Some html is allowed: b, blockquote@cite, em, i, strike, strong, sub, super, u)  

Enter the code shown (prevents robots):