-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ICH no longer works correctly on lines containing wide characters #14626
Comments
As far as I can tell, the regression came from the new The other thing I've noticed is that it only appears to be a problem when inserting a single space. An |
Thanks for digging into this @j4james! Leonard is out for a couple more weeks -- is this an area you'd be comfortable poking at in advance of the 1.17 release? |
The buffer code is a bit of a black box to me, so I don't want to promise anything, but I can take a look and see if there's an easy fix. I suspect this is out of my league though. |
First off, a few more test cases.
In all these cases, what we're doing is iterating over a range of cells in the buffer, and copying them from one location to another like this: do
{
const auto data = OutputCell(*textBuffer.GetCellDataAt(sourcePos));
textBuffer.Write(OutputCellIterator({ &data, 1 }), targetPos);
source.WalkInBounds(sourcePos, walkDirection);
} while (target.WalkInBounds(targetPos, walkDirection)); But when the source and target are only one cell apart, and the characters we're manipulating are occupying more than one cell, we can end up erasing a character before we've finished copying it. Let's say we've got a DBCS character in columns 1 and 2, which we're copying one column to the right, so we start by writing the trailing byte to column 3. However, because this is a two-cell character, the code immediately replaces both columns 2 and 3. But this leaves column 1 in a broken state (it's now half a DBCS character), so that gets erased as well. Now we come to copying what would have been the leading byte from column 1 to column 2, but it's column 1 is now a blank. And once we write a blank into column 2, that leaves column 3 in a broken state, so again that needs to be erased, and everything is blank. At least I that's my understanding of what's going on. I suspect the erasure of half-DBCS characters is a new thing that was added in the All we need to do is read two cells from the source, before writing to the target, so the code snippet from above would change to something like this: auto next = OutputCell(*textBuffer.GetCellDataAt(sourcePos));
do
{
const auto current = next;
source.WalkInBounds(sourcePos, walkDirection);
next = OutputCell(*textBuffer.GetCellDataAt(sourcePos));
textBuffer.WriteLine(OutputCellIterator({ ¤t, 1 }), targetPos);
} while (target.WalkInBounds(targetPos, walkDirection)); Now I realise this is not a long term fix - this will assumedly not work once we have characters that can span more than two cells. But it does at least gets things working again without requiring massive changes to the code. And once Leonard is back he can come up with a proper solution. |
When the buffer contains wide characters that occupy more than one cell, and those cells are scrolled horizontally by exactly one column, that operation can result in the wide characters being completely erased. This PR attempts to fix that bug, although it's not an ideal long term solution. Although not really to blame, it was PR #13626 that exposed this issue. The root of the problem is that scrolling operations copy cells one by one, but wide characters are written to the buffer two cells at a time. So when you move a wide character one position to the left or right, it can overwrite itself before it's finished copying, and the end result is the whole character gets erased. I've attempt to solve this by getting the affected operations to read two cells in advance before they start writing, so there's no risk of losing the source data before it's fully output. This may not work in the long term, with characters wider than two cells, but it should at least be good enough for now. I've also changed the `TextBuffer::Write` call to a `WriteLine` call to improve the handling of a wide character on the end of the line, where moving it right by one column would place it half off screen. It should just be dropped, but with the `Write` method, it would end up pushed onto the following line. ## Validation Steps Performed I've manually confirmed this fixes all the test cases described in #14626, and also added some unit tests that replicate those scenarios. Closes #14626
🎉This issue was addressed in #14650, which has now been successfully released as Handy links: |
Windows Terminal version
Commit 547349a
Windows build number
10.0.19044.2364
Other Software
No response
Steps to reproduce
printf "\u3053\u3093\u306B\u3061\u306F World\r\e[@\n"
Expected Behavior
This should write out the text
こんにちは World
, and then insert a space at the start of the line via anICH
sequence. This is what it looks like in Preview version 1.16.2641.0:Actual Behavior
When using the latest commit, the Japanese characters are erased by the
ICH
sequence, so all that's left isWorld
.So this looks to me like a regression, although I'm not sure when exactly it stopped working.
And note that this fails in OpenConsole as well. It's just easier to confirm that it's a recent regression in Windows Terminals because I know 1.16 was working OK.
The text was updated successfully, but these errors were encountered: