dahlbyk/posh-git — pull request #104 by jamesmanning (2013-06-20 05:26:57+00:00)
Title: initial code to show how async 'git status' prompt can work

This is intended more for discussion than being merged 'as-is' - if/when it were to be merged, the common bits of functionality in the sync and async code paths should be factored out (and maybe the sync case being the async code path with an added wait on the process to exit, but that's not really relevant right now).

The approach taken here intentionally doesn't use any of the other work (like caching status, filesystemwatcher, etc) - not that it couldn't be used with those, but more to keep it as simple as possible for now for a hopefully smaller diff and easier discussion about whether it's a good fit for posh-git.

The logic/data flow isn't much different than the existing synchronous prompt - it's controlled in this diff by a new bool in the GitPromptSettings (EnableAsyncFileStatus) for easier testing and comparison.

The key 'breakthrough' WRT earlier discussions in issues like #52 was finding that the RawUI interface didn't actually require cursor location manipulation in order to write to the screen buffer in a place other than wherever the cursor was already located. Instead, it supports writing a 2d rectangle to the buffer at a specified coordinate. This means we don't have to worry about the various bad states we might get into if we were manually moving the cursor and writing strings out to the buffer while the user was typing at their prompt (yay!)

[PSHostRawUserInterface.SetBufferContents Method (Coordinates, BufferCell[,])](http://msdn.microsoft.com/en-us/library/windows/desktop/ms568430.aspx)

The main new/entry function to look at it is Write-GitStatusPromptAsync in the new file GitAsyncPrompt.ps1

in the case of EnableAsyncFileStatus -eq $true (and we're in a repo that hasn't been disabled), it does:
- saves the current cursor position to know where it will write the multi-colored status string once it's done being calculated
- writes out a 'placeholder' string to the host on its own line (not strictly necessary, just to give the user an idea taht there's an ongoing process happening, even if it's only going to take a few seconds)
- run the same "git status" cmdline as usual, just doing so as a System.Diagnostics.Process
- gathers stdout lines via OutputDataReceived event on the process
- when Exited event happens on the process:
- takes the stdout lines, converts to the same object already being stored in $global:GitStatus by the existing prompt code, and also writes the object to that global variable to maintain that behavior
- calls Get-GitStatusBufferCells to convert that object into the set of buffer cells (multi-colored string, currently always 1 row tall just like the existing synchronous code)
- calls Write-BufferCellsToPosition to write those buffer cells to the screen buffer (correct location was grabbed/stored earlier)

IME it runs 'fast enough' that even if it hit enter ~10 times, resulting in ~10 git status calls running, they all still complete very quickly (and they each write to their correct screen buffer location).

Does the idea/approach make sense for posh-git?

Thanks!

Related issues about perf of git status:

- #78 - posh-git is still too slow...
- #52 - Cache Get-GitStatus results to speed up the prompt
- #51 - asynchronous git prompt updates
- #50 - Posh-git is too slow