Repository: microsoft/terminal (language: cpp)

Issue:
Spaces added to prompt on shell launch (PowerShell/pwsh)
# Environment

```none
Win32NT 10.0.19042.0 Microsoft Windows NT 10.0.19042.0
Windows Terminal Version: 1.4.3141.0

https://github.com/justjanne/powerline-go

```

# Steps to reproduce

Add the following to your PowerShell $PROFILE (adjust the path to the powerline-go exectuable to your local environment)

```PowerShell
Function global:prompt {
    $pwd = $ExecutionContext.SessionState.Path.CurrentLocation
    $startInfo = New-Object System.Diagnostics.ProcessStartInfo
    $startInfo.FileName = "$ENV:OneDriveConsumer\Util\powerline-go.exe"
    $startInfo.Arguments = "-shell bare"
    $startInfo.Environment["TERM"] = "xterm-256color"
    $startInfo.CreateNoWindow = $true
    $startInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8
    $startInfo.RedirectStandardOutput = $true
    $startInfo.UseShellExecute = $false
    $startInfo.WorkingDirectory = $pwd
    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $startInfo
    $process.Start() | Out-Null
    $standardOut = $process.StandardOutput.ReadToEnd()
    $process.WaitForExit()
    $standardOut
}
```

Start a PowerShell (classic) or PowerShell terminal window via Windows Terminal. The first time the prompt function is called there are spaces inserted between the different prompt parts. Pressing `Enter` makes these disappear and the prompt works fine.

If I use the same profile without Windows Terminal launching PowerShell.exe directly it works fine. So I suspect this is related to Windows Terminal.

Here's an animated GIF that shows what is going on:

![powerline-spaces](https://user-images.githubusercontent.com/862546/99776375-013ac800-2b11-11eb-8c05-48a7c3f953ed.gif)


# Expected behavior

I expect to see the prompt formatted correctly as follows:
<img width="720" alt="powerline-go-expected" src="https://user-images.githubusercontent.com/862546/99776755-9c33a200-2b11-11eb-9cbe-441844632c51.png">


# Actual behavior

This is what the prompt looks like when the PowerShell is first started:

<img width="720" alt="powerline-go-actual" src="https://user-images.githubusercontent.com/862546/99776660-70182100-2b11-11eb-963f-c907c4c9ffa0.png">



Discussion / hints:
This might sound crazy, but if you highlight that first prompt line, the one with the spaces, then dismiss the selection, does it re-render correctly? 
It looks like you've customized your powershell profile somewhat? I can't reproduce this, but I am not using `-NoLogo` or have otherwise suppressed the startup banner. 🤔 
Yeah, this only happens with `-NoLogo`. Interesting!

@zadjii-msft this is coming out of conpty with additional `\e[1C` in it, but only on the first-ever print to the screen. _Everything else_ comes out fine.

First run

```
␛[38;5;250m␛[48;5;240m␛[H␣REDMOND\duhowett␛[38;5;240m␛[48;5;238m␛[1C␛[38;5;250m␛[1Cdhowett-sl␛[38;5;238m␛[48;5;31m␛[1C␛[38;5;15m␛[1C~␛[38;5;31m␛[48;5;236m␛[1C␛[38;5;15m␛[1C$␛[38;5;236m␛[49m␛[1C␛[1C␛
```

After Clear

```
␛[38;5;250m␛[48;5;240m␣REDMOND\duhowett␣␛[38;5;240m␛[48;5;238m␣␛[38;5;250mdhowett-sl␣␛[38;5;238m␛[48;5;31m␣␛[38;5;15m~␣␛[38;5;31m␛[48;5;236m␣␛[38;5;15m$␣␛[38;5;236m␛[49m␣␛[m
```
well that's batty. Thanks for investigating! @DHowett any chance you've got a oneliner repro for me that I can turn into a test? (if not that's cool too, this is probably enough to work off of)


EDIT: J/K read the OP again, that's simple enough. I was worried I had to install a module or something.
Nah, it's even easier!

### `test_8341.ps1`

```
"`e[38;5;250m`e[48;5;240m REDMOND\duhowett `e[38;5;240m`e[48;5;238m `e[38;5;250mdhowett-sl `e[38;5;238m`e[48;5;31m `e[38;5;15m~ `e[38;5;31m`e[48;5;236m `e[38;5;15m$ `e[38;5;236m`e[49m `e[m"
sleep 100
```

###

```
wt nt pwsh -c test_8341.ps1
```
Just so long as it's the first thing printed to the screen. I used the powershell core escape escape, so it won't work in Windows PowerShell (sorry)
I've been off-line for a while but thanks for the looking into this :-)
hmm.. Have the same thing happening to me with Windows terminal Version: 1.4.3243.0 but with Ubuntu wsl after following this guide [https://docs.microsoft.com/sv-se/windows/terminal/tutorials/powerline-setup] .
Works fine with wsl.exe.

Resizing the window or open any file and close it will fix the stripes.

My Windows Terminal was updated yesterday and this started happening for me. Even though I always use up to date versions of everything (or did so until 2 months ago), this never happened before.

Any update on this?
I can confirm that this happens with the `-NoLogo` option, and only on the first prompt:

![image](https://user-images.githubusercontent.com/63101447/132992092-7bba28df-d230-46fd-95dc-01b27e278cba.png)

As a workaround, I've added a `cls` to my PS profile, which had no impact on launch time:

```powershell
Import-Module posh-git
Import-Module oh-my-posh
Set-PoshPrompt Paradox
Clear-Host
```
How strange! Would you be willing to try and capture this with the [debug tap](https://github.com/microsoft/terminal/wiki/Enabling-the-debug-tap)?
Wait, I forgot that we already had a repro for this. You can ignore that request!
@DHowett if you need any assistance in reproducing, or more information on it, count on me! I'm interested in helping in any way I can ;) 
ah, I have the same problem. But it perfoms normally before, just becomes like this when I accidentally change the cmd profile. Then my wsl looks like this.
> My Windows Terminal was updated yesterday and this started happening for me. Even though I always use up to date versions of everything (or did so until 2 months ago), this never happened before.
> 
> Any update on this?

Maybe because of the update? Me too. It jumps out recently.
It happens only start up, and has nothing to do with the logo, I think.
![image](https://user-images.githubusercontent.com/59756623/133552772-d5e0095a-6198-492f-ad9e-e914d2f74035.png)


If there's anything before prompt, it works fine, even print some space before the prompt.
![image](https://user-images.githubusercontent.com/59756623/133553267-fd5bbda6-2c00-4c1a-a866-2302bfc90c0f.png)

I am also affected. I'm using Powerline Go
https://github.com/justjanne/powerline-go

Also, when split the window, it will look like this:
![image](https://user-images.githubusercontent.com/59756623/133628181-c2404aa1-752c-4ef1-a34d-891a745d4c17.png)

Same issue for me. I'm using powerline-shell in Ubuntu. This issue only occurs in Windows Terminal and not in any other terminal. Including Hyper terminal.

![image](https://user-images.githubusercontent.com/874394/134835167-8211168d-c185-4ee3-8af4-264d33611516.png)

This is a fresh install of Windows Terminal and like others it only happens on first render. If I hit enter for a new prompt it renders correctly. The only setting I changed is to set the font to `Fira Code` which has the separator symbols in it and I set the cursor style to a solid bar. That's it. The rest is all default, though I have toggled plenty of render/appearance settings back and forth to see if anything affected it. No settings seem to affect it.
> I can confirm that this happens with the `-NoLogo` option, and only on the first prompt:
> 
> ![image](https://user-images.githubusercontent.com/63101447/132992092-7bba28df-d230-46fd-95dc-01b27e278cba.png)
> 
> As a workaround, I've added a `cls` to my PS profile, which had no impact on launch time:
> 
> ```powershell
> Import-Module posh-git
> Import-Module oh-my-posh
> Set-PoshPrompt Paradox
> Clear-Host
> ```

I just did the equivalent in my `.bashrc` for Ubuntu by putting a `clear;` at the bottom and this does "fix" the issue! So thanks for that. At least there's a workaround. It's a minor issue I know but it was annoying me lol
The issue seems to be resolved for me after switching to "WSL from Microsoft Store".
https://devblogs.microsoft.com/commandline/a-preview-of-wsl-in-the-microsoft-store-is-now-available/
Oh wow thanks for the link. Happy to see WSL progressing into a stand-alone app!
Just to be complete, this also happens when I open the Ubnutu / WSL2 / Bash shell in Terminal.
This problem still happens in Windows Terminal 1.12.3472.0
HA! We finally have a root cause for this. I ran into a different issue, where the "version update" notification was losing the background color around the edges, and it all started to fall into place.

<img width="496" alt="image" src="https://user-images.githubusercontent.com/189190/152048335-123fbc32-56ed-45a1-b066-f970f2dec9de.png">

If we are painting the first frame or any frame after you clear the entire screen, as an optimization[^1] we don't send a bunch of space characters. We neglected to check whether those space characters were _meaningful_ (had background colors, underlines, other attributes...). This happens for each run of text (so, region of text bordered by a color/attribute change), not per line.

Proximal area of failure:

https://github.com/microsoft/terminal/blob/27de97bf9325d02c6e5bf13e005e2c805ac963a3/src/renderer/vt/paint.cpp#L448-L462

I wonder if we can use this function to help:

https://github.com/microsoft/terminal/blob/27de97bf9325d02c6e5bf13e005e2c805ac963a3/src/buffer/out/TextAttribute.hpp#L135-L137

[^1] The ConPTY API tries very hard to pretend that it is not translating a buffer of 120x30 cells into VT . . . so it lies, cheats and steals sometimes. The buffer contains 3900 empty spaces on startup, so we try not to send them to apps.
<img width="278" alt="image" src="https://user-images.githubusercontent.com/189190/154380486-d4ee12af-7b9f-4c69-adf9-c8ab30728a20.png">

It's testable!
I am not certain the right way to fix it, but it's fixable too. The test passes as of https://github.com/microsoft/terminal/commit/c9e206997106eeecbaa287d9f41e4ccdd6f835e1, but the attribute math is not correct (to which I suspect @j4james will attest)

Fix patch:
diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
index a497a826597..e2efc4788d5 100644
--- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
+++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
@@ -227,6 +227,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
     TEST_METHOD(SimpleAltBufferTest);
     TEST_METHOD(AltBufferToAltBufferTest);
 
+    TEST_METHOD(TestPowerLineFirstFrame);
+
     TEST_METHOD(AltBufferResizeCrash);
 
 private:
@@ -4108,6 +4110,77 @@ void ConptyRoundtripTests::AltBufferToAltBufferTest()
     verifyBuffer(*termAltTb, term->_GetMutableViewport().ToExclusive(), Frame::StillInAltBuffer);
 }
 
+void ConptyRoundtripTests::TestPowerLineFirstFrame()
+{
+    Log::Comment(L"This is a test for GH#8341. If we received colored spaces "
+                 L"BEFORE the first frame, we should still emit them!");
+
+    auto& g = ServiceLocator::LocateGlobals();
+    auto& renderer = *g.pRender;
+    auto& gci = g.getConsoleInformation();
+    auto& si = gci.GetActiveOutputBuffer();
+    auto& sm = si.GetStateMachine();
+
+    auto* hostTb = &si.GetTextBuffer();
+    auto* termTb = term->_mainBuffer.get();
+
+    _checkConptyOutput = false;
+
+    TextAttribute whiteOnGreen{};
+    whiteOnGreen.SetIndexedForeground(TextColor::DARK_WHITE);
+    whiteOnGreen.SetIndexedBackground(TextColor::BRIGHT_GREEN);
+
+    TextAttribute greenOnBlack{};
+    greenOnBlack.SetIndexedForeground(TextColor::BRIGHT_GREEN);
+    greenOnBlack.SetIndexedBackground(TextColor::BRIGHT_BLACK);
+
+    TextAttribute whiteOnBlack{};
+    whiteOnBlack.SetIndexedForeground(TextColor::DARK_WHITE);
+    whiteOnBlack.SetIndexedBackground(TextColor::BRIGHT_BLACK);
+
+    TextAttribute blackOnDefault{};
+    blackOnDefault.SetIndexedForeground(TextColor::BRIGHT_BLACK);
+
+    TextAttribute defaultOnDefault{};
+
+    Log::Comment(L"========== Fill test content ==========");
+
+    // As a pwsh one-liner:
+    //
+    //  "`e[37m`e[102m foo\bar `e[92m`e[100m▶ `e[37mBar `e[90m`e[49m▶ `e[m"
+    //
+    // Generally taken from
+    // https://github.com/microsoft/terminal/issues/8341#issuecomment-731310022,
+    // but minimized for easier testing.
+
+    sm.ProcessString(L"\x1b[37m\x1b[102m" // dark white on bright green
+                     L" foo\\bar ");
+    sm.ProcessString(L"\x1b[92m\x1b[100m" // bright green on bright black
+                     L"▶ ");
+    sm.ProcessString(L"\x1b[37m" // dark white on bright black
+                     L"Bar ");
+    sm.ProcessString(L"\x1b[90m\x1b[49m" // bright black on default
+                     L"▶ ");
+    sm.ProcessString(L"\x1b[m\n"); // default on default
+
+    auto verifyBuffer = [&](const TextBuffer& tb) {
+        // If this test fails on character 8, then it's because we didn't emit the space, we just moved ahead.
+        auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, whiteOnGreen, 9u);
+        TestUtils::VerifyLineContains(iter0, OutputCellIterator{ greenOnBlack, 2u });
+        TestUtils::VerifyLineContains(iter0, OutputCellIterator{ whiteOnBlack, 4u });
+        TestUtils::VerifyLineContains(iter0, OutputCellIterator{ blackOnDefault, 2u });
+    };
+
+    Log::Comment(L"========== Check host buffer ==========");
+    verifyBuffer(*hostTb);
+
+    Log::Comment(L"========== Paint first frame ==========");
+    VERIFY_SUCCEEDED(renderer.PaintFrame());
+
+    Log::Comment(L"========== Check terminal buffer ==========");
+    verifyBuffer(*termTb);
+}
+
 void ConptyRoundtripTests::AltBufferResizeCrash()
 {
     Log::Comment(L"During the review for GH#12719, it was noticed that this "
diff --git a/src/host/ut_host/ConptyOutputTests.cpp b/src/host/ut_host/ConptyOutputTests.cpp
index 29e0e28f20f..1ebff63cac2 100644
--- a/src/host/ut_host/ConptyOutputTests.cpp
+++ b/src/host/ut_host/ConptyOutputTests.cpp
@@ -121,6 +121,7 @@ class ConptyOutputTests
     TEST_METHOD(WriteAFewSimpleLines);
     TEST_METHOD(InvalidateUntilOneBeforeEnd);
     TEST_METHOD(SetConsoleTitleWithControlChars);
+    TEST_METHOD(IncludeBackgroundColorChangesInFirstFrame);
 
 private:
     bool _writeCallback(const char* const pch, const size_t cch);
@@ -371,6 +372,7 @@ void ConptyOutputTests::SetConsoleTitleWithControlChars()
 {
     BEGIN_TEST_METHOD_PROPERTIES()
         TEST_METHOD_PROPERTY(L"Data:control", L"{0x00, 0x0A, 0x1B, 0x80, 0x9B, 0x9C}")
+        TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
     END_TEST_METHOD_PROPERTIES()
 
     int control;
@@ -400,3 +402,29 @@ void ConptyOutputTests::SetConsoleTitleWithControlChars()
 
     VERIFY_SUCCEEDED(renderer.PaintFrame());
 }
+
+void ConptyOutputTests::IncludeBackgroundColorChangesInFirstFrame()
+{
+    auto& g = ServiceLocator::LocateGlobals();
+    auto& renderer = *g.pRender;
+    auto& gci = g.getConsoleInformation();
+    auto& si = gci.GetActiveOutputBuffer();
+    auto& sm = si.GetStateMachine();
+
+    sm.ProcessString(L"\x1b[41mRun 1     \x1b[42mRun 2     \x1b[43mRun 3     \x1b[m");
+
+    expectedOutput.push_back("\x1b[2J"); // standard init sequence for the first frame
+    expectedOutput.push_back("\x1b[m"); // standard init sequence for the first frame
+    expectedOutput.push_back("\x1b[41m");
+    expectedOutput.push_back("\x1b[H"); // standard init sequence for the first frame
+    expectedOutput.push_back("Run 1     ");
+    expectedOutput.push_back("\x1b[42m");
+    expectedOutput.push_back("Run 2     ");
+    expectedOutput.push_back("\x1b[43m");
+    expectedOutput.push_back("Run 3     ");
+
+    // This is also part of the standard init sequence.
+    expectedOutput.push_back("\x1b[?25h");
+
+    VERIFY_SUCCEEDED(renderer.PaintFrame());
+}
diff --git a/src/inc/TestUtils.h b/src/inc/TestUtils.h
index de8a94f35e8..d6e8f146593 100644
--- a/src/inc/TestUtils.h
+++ b/src/inc/TestUtils.h
@@ -168,15 +168,19 @@ class TerminalCoreUnitTests::TestUtils
             auto actualAttrs = actual->TextAttr();
             auto expectedAttrs = expected->TextAttr();
 
-            auto mismatched = (actualChars != expectedChars || actualAttrs != expectedAttrs);
+            auto mismatched = ((!expectedChars.empty() && actualChars != expectedChars) || actualAttrs != expectedAttrs);
             if (mismatched)
             {
                 WEX::Logging::Log::Comment(WEX::Common::NoThrowString().Format(
                     L"Character or attribute at index %d was mismatched", charsProcessed));
             }
 
-            VERIFY_ARE_EQUAL(expectedChars, actualChars);
+            if (!expectedChars.empty())
+            {
+                VERIFY_ARE_EQUAL(expectedChars, actualChars);
+            }
             VERIFY_ARE_EQUAL(expectedAttrs, actualAttrs);
+
             if (mismatched)
             {
                 return false;
diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp
index fade9487eb8..83b73fbd6bf 100644
--- a/src/renderer/vt/paint.cpp
+++ b/src/renderer/vt/paint.cpp
@@ -448,10 +448,13 @@ using namespace Microsoft::Console::Types;
     // the lines _wrapped_. It doesn't care to manually break the lines, but if
     // we trimmed the spaces off here, we'd print all the "~"s one after another
     // on the same line.
-    const auto removeSpaces = !lineWrapped && (useEraseChar ||
-                                               _clearedAllThisFrame ||
-                                               (_newBottomLine && printingBottomLine && bgMatched));
-    const auto cchActual = removeSpaces ? nonSpaceLength : cchLine;
+    static const TextAttribute defaultAttrs{};
+    const bool removeSpaces = !lineWrapped && (useEraseChar // we determined earlier that ECH is optimal
+                                               || (_clearedAllThisFrame && _lastTextAttributes == defaultAttrs) // OR we cleared the last frame to the default attributes (specifically)
+                                               || (_newBottomLine && printingBottomLine && bgMatched)); // OR we just scrolled a new line onto the bottom of the screen with the correct attributes
+    const size_t cchActual = removeSpaces ?
+                                 (cchLine - numSpaces) :
+                                 cchLine;
 
     const auto columnsActual = removeSpaces ?
                                    (totalWidth - numSpaces) :


Test patch: