JanDeDobbeleer / oh-my-posh2

A prompt theming engine for Powershell
MIT License
5.19k stars 284 forks source link

First prompt doesn't color properly #264

Closed judgeaxl closed 4 years ago

judgeaxl commented 4 years ago

For some reason the first prompt I get when opening a new powershell core (7) prompt doesn't color properly and instead just prints the escape codes into the terminal. Pressing enter gives a new prompt with the proper colors. This happens with a stand-alone powershell core terminal as well as one hosted in Windows Terminal. If I remote Set-Theme xxx from my startup script and run it manually after the first non-themed prompt, the prompt is fine.

This is what I get: ←[97m←[40m ←[39;49m←[30m←[44m←[39;49m←[97m←[44m ~ ←[39;49m←[34m←[40m←[39;49m ←]2;C:...\Users\axl←]9;9;"C:\Users\axl"

I dug around a bit in posh-git and the prompt theme code and my only guess is that the AnsiTerminal variable for some reason evaluates to the wrong value as the startup script is running.

JanDeDobbeleer commented 4 years ago

I haven't seen this issue yet, can you try to adjust the code there to see what it contains so we can troubleshoot this together?

judgeaxl commented 4 years ago

I tried logging in the Set-Prompt and Write-Prompt methods, and Test-IsVanillaWindow returns false, so it goes on to use the fully decorated version.

Additionally it also happens in the Visual Studio Code terminal, so seems like a general problem while the startup scripts are running.

@JanDeDobbeleer What changes would you like me to test?

judgeaxl commented 4 years ago

It's Test-AnsiTerminal that returns true and enables coloring.

And if I run other Write-Host commands with e.g. -ForegroundColor Red inside Set-Prompt they get colored, even though the prompt then doesn't, as in the original message above

JanDeDobbeleer commented 4 years ago

This is something different as I don't use ANSI escape sequences to colorize the prompt. I'm clueless right now tbh.

judgeaxl commented 4 years ago

OK, I was assuming the color codes passed to Write-Host somehow got translated internally into ANSI escape sequences, but I must say that's just a guess, since that's what I'm seeing. I'll try to strip down my profile to the bare minimum to get an isolated repro case.

I also noticed that if I start a sub-shell, using something like pipenv, then the first prompt has the same issue.

judgeaxl commented 4 years ago

OK, I've figured out what causes it, but I'm not sure why yet.

In order to split up my Powershell environment a bit I've got this snippet which loads individual scripts, and setting up oh-my-posh is in one of those. If I move the prompt config out to the root script instead, it works as intended.

$psdir="$HOME\tools\PSScripts"
Get-ChildItem "${psdir}\autoload\*.ps1" | %{.$_}

So there's probably something related to these scripts being loaded in this way that causes the problem. I grabbed this snippet off of the internet a long time ago, so I'm not sure exactly what alternatives there are, but at least I know where to look now.

I'll post a solution here if I come up with one, that doesn't involve moving the code out.

Thanks!

judgeaxl commented 4 years ago

I've isolated this down to running posh-git's Start-SshAgent and/or ssh-add.exe in the Powershell startup script, but I still don't understand why. I'm going to have to drop the research for a bit now, but I'll continue digging.

sql-sith commented 3 years ago

This has been happening on my laptops also. Cygwin has never been installed on either of them (as some others found problems with) and I've never had my prompt code moved out to an auxiliary script (as 2judgeaxl did). However, I did have my prompt function defined right before running Start-SshAgent as well as several calls to ssh-add.

Taking a cue from @judgeaxl 's comment above, I commented out the ssh-related code and my first prompt was correct. Uncomment that section and it was all messed up.

What I finally narrowed it down to was one statement in my pwsh $PROFILE. This is it: $private_keys = @(ssh-add -l | ForEach-Object { $_.Split(' ')[2] } | ForEach-Object { Split-Path -Path $_ -Leaf })

I used that line of code to get the names of all my loaded private keys. Then I would loop through the public keys in my .ssh directory and add them, but only if they were not already in $private_keys.

To fix this, I simply removed the line listed above. The only side-effect is that if my profile is run in an environment where my keys have already been loaded, then ssh-add -l will post a message telling me this. This is not a problem at all, compared to the gibberish that was showing up in my terminal before.

@judgeaxl , thanks for pointing me toward posh-git and/or ssh-add. Here is the code I wound up with. I have left the offending line in the code, commented out, so you can see where it was.

###############################################################
## Load SSH identities:
###############################################################
# setup SSH Agent:
Write-Host "`nLoading default SSH identity. . ."
Start-SshAgent 

# grab the already-loaded private keys:
# $private_keys = @(ssh-add -l | ForEach-Object { $_.Split(' ')[2] } | ForEach-Object { Split-Path -Path $_ -Leaf })

# add private keys that are not already loaded:
foreach ($public_key in ((Get-ChildItem ~/.ssh/*.pub))) {

    $private_key = $public_key.Name.Substring(0, $public_key.Name.LastIndexOf('.'))

    if ($private_keys -contains $private_key) {
        Write-Output ('Identity for private key {0} is already loaded.' -f $private_key)
    }
    else {        
        Write-Output ('Loading identity for private key {0}.' -f $private_key)
        ssh-add ('~/.ssh/' + $private_key)
        $private_keys += $private_key
        ""
    }
}

Remove-Item variable:private_key, variable:private_keys, variable:public_key

"`nHere are the RSA identities that have been loaded:"
ssh-add -l