Posh-git: Write-VcsStatus behaving differently

Created on 29 Aug 2018  路  7Comments  路  Source: dahlbyk/posh-git

  • posh-git version/path: 1.0.0.0 beta2x C:\source\posh-git\src
  • PowerShell version: 5.1.17134.228
  • git version 2.18.0.windows.1
  • OS: Microsoft Windows NT 10.0.17134.0

Issue Description

I have a custom multi-lines prompt that used to work with v0.6.1.20160330 of posh-git. The git status stays on the first line. The second line contains just the right angle bracket. Now I have installed it on a new machine, and git status is appearing in the second line.

Here's the content of my profile script:

if(!(Test-Path function:\TabExpansion)) { New-Item function:\Global:TabExpansion -value '' | Out-Null }

function Test-Administrator {
    $user = [Security.Principal.WindowsIdentity]::GetCurrent();
    (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

function prompt {
    $realLASTEXITCODE = $LASTEXITCODE

    Write-Host

    if (Test-Administrator) {  # Use different username if elevated
        Write-Host "^" -NoNewline -ForegroundColor White
    }

    Write-Host "$ENV:USERNAME@" -NoNewline -ForegroundColor DarkYellow
    Write-Host "$ENV:COMPUTERNAME" -NoNewline -ForegroundColor Magenta

    if ($s -ne $null) {  # color for PSSessions
        Write-Host " (`$s: " -NoNewline -ForegroundColor DarkGray
        Write-Host "$($s.Name)" -NoNewline -ForegroundColor Yellow
        Write-Host ") " -NoNewline -ForegroundColor DarkGray
    }

    Write-Host ":" -NoNewline -ForegroundColor DarkGray
    Write-Host $($(Get-Location) -replace ($env:USERPROFILE).Replace('\','\\'), "~") -NoNewline -ForegroundColor Blue
    Write-Host ":" -NoNewline -ForegroundColor DarkGray
    Write-Host (Get-Date -Format "d-MMM HH:mm:ss") -NoNewline -ForegroundColor DarkMagenta
    Write-Host ":" -NoNewline -ForegroundColor DarkGray

    $global:LASTEXITCODE = $realLASTEXITCODE

    Write-VcsStatus

    Write-Host ""

    return "> "
}

function which($command) {
  get-command $command | Format-Table Path
}

Set-Alias which gcm

Import-Module 'C:\source\posh-git\src\posh-git.psd1'

I have somewhat limited understanding of Powershell and can't remember how I cobbled up that prompt. What I was trying to achieve there is an updated timestamp in the prompt every time a command is executed.

Any ideas why Write-VcsStatus now inserts a newline before it?

Most helpful comment

If you want to use your old prompt function, change it to this:

function prompt {
    $realLASTEXITCODE = $LASTEXITCODE

    Write-Host

    if (Test-Administrator) {  # Use different username if elevated
        Write-Host "^" -NoNewline -ForegroundColor White
    }

    Write-Host "$ENV:USERNAME@" -NoNewline -ForegroundColor DarkYellow
    Write-Host "$ENV:COMPUTERNAME" -NoNewline -ForegroundColor Magenta

    if ($s -ne $null) {  # color for PSSessions
        Write-Host " (`$s: " -NoNewline -ForegroundColor DarkGray
        Write-Host "$($s.Name)" -NoNewline -ForegroundColor Yellow
        Write-Host ") " -NoNewline -ForegroundColor DarkGray
    }

    Write-Host ":" -NoNewline -ForegroundColor DarkGray
    Write-Host $($(Get-Location) -replace ($env:USERPROFILE).Replace('\','\\'), "~") -NoNewline -ForegroundColor Blue
    Write-Host ":" -NoNewline -ForegroundColor DarkGray
    Write-Host (Get-Date -Format "d-MMM HH:mm:ss") -NoNewline -ForegroundColor DarkMagenta
    Write-Host ":" -NoNewline -ForegroundColor DarkGray

    $global:LASTEXITCODE = $realLASTEXITCODE

    "$(Write-VcsStatus)> "
}

Essentially the Write-VcsStatus returns a string in v1.x when ANSI mode is enabled ($GitPromptSettings.AnsiConsole is $true). That contains the string to display. This string has VT char sequences in it to color the text. However when your prompt function emits two strings e.g.:

    Write-VcsStatus # output string here

    Write-Host ""

    return "> " # output string here

That object array outuput is not accepted by PowerShell. You can see this by executing:

PS> Get-Member -InputObject (prompt)

   TypeName: System.Object[]

So outputting just "$(Write-VcsStatus)> " results in the prompt function output being a single string.

All 7 comments

A lot has changed WRT to the way the Git status is written in order to support ANSI capable hosts. I have a way to fix you prompt function above but I think you'd be better off just using the new customization support in v1. Put this settings right after you import posh-git:

$GitPromptSettings.DefaultPromptPrefix.Text = "$(if (Test-Administrator) {'^'})${env:USERNAME}@${env:COMPUTERNAME} "
$GitPromptSettings.DefaultPromptPrefix.ForegroundColor = [System.ConsoleColor]::DarkYellow
$GitPromptSettings.PathStatusSeparator.Text = " $(Get-Date -Format "d-MMM HH:mm:ss") "
$GitPromptSettings.PathStatusSeparator.ForegroundColor = [System.ConsoleColor]::DarkMagenta

$GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true
$GitPromptSettings.DefaultPromptPath.ForegroundColor = [System.ConsoleColor]::Blue

If you want to use your old prompt function, change it to this:

function prompt {
    $realLASTEXITCODE = $LASTEXITCODE

    Write-Host

    if (Test-Administrator) {  # Use different username if elevated
        Write-Host "^" -NoNewline -ForegroundColor White
    }

    Write-Host "$ENV:USERNAME@" -NoNewline -ForegroundColor DarkYellow
    Write-Host "$ENV:COMPUTERNAME" -NoNewline -ForegroundColor Magenta

    if ($s -ne $null) {  # color for PSSessions
        Write-Host " (`$s: " -NoNewline -ForegroundColor DarkGray
        Write-Host "$($s.Name)" -NoNewline -ForegroundColor Yellow
        Write-Host ") " -NoNewline -ForegroundColor DarkGray
    }

    Write-Host ":" -NoNewline -ForegroundColor DarkGray
    Write-Host $($(Get-Location) -replace ($env:USERPROFILE).Replace('\','\\'), "~") -NoNewline -ForegroundColor Blue
    Write-Host ":" -NoNewline -ForegroundColor DarkGray
    Write-Host (Get-Date -Format "d-MMM HH:mm:ss") -NoNewline -ForegroundColor DarkMagenta
    Write-Host ":" -NoNewline -ForegroundColor DarkGray

    $global:LASTEXITCODE = $realLASTEXITCODE

    "$(Write-VcsStatus)> "
}

Essentially the Write-VcsStatus returns a string in v1.x when ANSI mode is enabled ($GitPromptSettings.AnsiConsole is $true). That contains the string to display. This string has VT char sequences in it to color the text. However when your prompt function emits two strings e.g.:

    Write-VcsStatus # output string here

    Write-Host ""

    return "> " # output string here

That object array outuput is not accepted by PowerShell. You can see this by executing:

PS> Get-Member -InputObject (prompt)

   TypeName: System.Object[]

So outputting just "$(Write-VcsStatus)> " results in the prompt function output being a single string.

In the process of coming up with a $GitPromptSettings customization to get you your exact prompt (sans the PSSession bit - couldn't tell exactly where $s was coming from), I found a few bugs in v1 that I'd like to fix. The following won't work right now but it should after I get a few changes merged in. After those changes are merged, the following will work:

$GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true
$GitPromptSettings.DefaultPromptPath.Text = $null
$GitPromptSettings.DefaultPromptPrefix.CustomAnsi =
    "`n`$(Get-PromptConnectionInfo -Format '[{1}@{0}]: ')" +
    "`$(if (global:Test-Administrator) {`"$([char]27)[37m^`"})" +
    "$([char]27)[33m`${env:USERNAME}@$([char]27)[95m`${env:COMPUTERNAME}$([char]27)[90m:" +
    "$([char]27)[94m`$(Get-PromptPath)$([char]27)[90m:" +
    "$([char]27)[35m`$(Get-Date -Format `"d-MMM HH:mm:ss`")$([char]27)[90m:"  

This will give a prompt like this:

image

BTW if you ran only on PS Core, you could use the esc char e support to simplify the one setting:

# ONLY WORKS IN POWERSHELL CORE AFTER FIXES MERGED
$GitPromptSettings.DefaultPromptPrefix.CustomAnsi =
    "`n`$(Get-PromptConnectionInfo -Format '[{1}@{0}]: ')" +
    "`$(if (global:Test-Administrator) {`"`e[37m^`"})" +
    "`e[33m`${env:USERNAME}@`e[95m`${env:COMPUTERNAME}`e[90m:" +
    "`e[94m`$(Get-PromptPath)`e[90m:" +
    "`e[35m`$(Get-Date -Format `"d-MMM HH:mm:ss`")`e[90m:" 

Thank you for all that. I ended up modifying my prompt function as you suggested, adding a newline character in the final output to be $(Write-VcsStatus) and >. Now it works just like before! I used this method because I would like this prompt even if I didn't have posh-git installed. I can just remove the call to Write-VcsStatus in that case.

@dahlbyk I think this actually reveals a subtle bug that could trip up folks moving from v0.x to v1 and are running in a host without VT support. This is somewhat of a corner case scenario given the v1 requires PS v5. But if someone has v5 installed on Win7, they could hit it. The issue is this. In v0.x, Write-VcsStatus returned nothing. It simply wrote to the host. So this part of the OP's prompt worked fine because it output a single string >:

    Write-Host ""
    return "> "

However, in v1 when AnsiConsole is $false and we use Write-Host, we still output a single, empty string from Write-VcsStatus. That changes the function above to output [object[]] now with an empty string in ndx 0 and > at ndx 1. That breaks PowerShell's prompt handling. It gets confused by the prompt function outputting something other than a single string.

In order to fix this, we'd have to modify Write-GitStatus and Write-VcsStatus to not output anything - not even an empty string - when AnsiConsole is $false. Is it worth it?

In order to fix this, we'd have to modify Write-GitStatus and Write-VcsStatus to not output anything - not even an empty string - when AnsiConsole is $false. Is it worth it?

This seems like it should be simple to fix, isn't it?

I think so. I'll submit a PR so we can see what the changes look like.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ewgoforth picture ewgoforth  路  8Comments

arafat-ar13 picture arafat-ar13  路  8Comments

Alfetta159 picture Alfetta159  路  3Comments

jpierson picture jpierson  路  9Comments

exbuzz picture exbuzz  路  7Comments