Skip to content

Commit d69c70c

Browse files
[terminal] Fixes writing to a non-empty line sometimes destroying the contents of that line (#702).
1 parent 7cf09ba commit d69c70c

File tree

3 files changed

+32
-9
lines changed

3 files changed

+32
-9
lines changed

Changelog.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
### 0.3.2 (unreleased)
22

33
- Adds E3 capability, so `clear` now defaults to clearing screen and scrollback (#693).
4+
- Fixes writing to a non-empty line sometimes destroying the contents of that line (#702).
5+
- Fixes underline decoration for wide character cells.
46
- Fixes SGR 8 (Conceal/Hidden) attribute doesn't work as expected (#699).
57
- Fixes Synchronized Updates (`SM/RM ? 2026`) sometimes lagging behind in rendering.
68
- Fixes SGR and text breakage when altering charsets via `ESC ( 0` VT sequence (#661).
@@ -22,7 +24,6 @@
2224
- and more...
2325
- Adds specialized PTY implementation for Linux operating system utilizing OS-specific kernel APIs.
2426
- Changes CLI syntax for `contour parser-table` to `contour generate parser-table`.
25-
- Fixes underline decoration for wide character cells.
2627

2728
### 0.3.1 (2022-05-01)
2829

src/terminal/Line.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class Line
296296
}
297297
[[nodiscard]] bool isInflatedBuffer() const noexcept
298298
{
299-
return std::holds_alternative<TrivialBuffer>(storage_);
299+
return !std::holds_alternative<TrivialBuffer>(storage_);
300300
}
301301

302302
void setBuffer(TrivialBuffer const& buffer) noexcept { storage_ = buffer; }

src/terminal/Screen.cpp

+29-7
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,35 @@ size_t Screen<Cell, TheScreenType>::emplaceCharsIntoCurrentLine(string_view _cha
339339
auto columnsAvailable = (_state.margin.horizontal.to.value + 1) - _state.cursor.position.column.value;
340340
auto const charsToWrite = static_cast<size_t>(min(columnsAvailable, static_cast<int>(_chars.size())));
341341

342-
currentLine().reset(_state.cursor.graphicsRendition,
343-
_state.cursor.hyperlink,
344-
crispy::BufferFragment {
345-
_terminal.currentPtyBuffer(),
346-
_chars.substr(0, charsToWrite),
347-
});
348-
advanceCursorAfterWrite(ColumnCount::cast_from(charsToWrite));
342+
Line<Cell>& line = currentLine();
343+
if (!line.isInflatedBuffer() && line.empty())
344+
{
345+
// Only use fastpath if the currently line hasn't been inflated already.
346+
// Because we might lose prior-written textual/SGR information otherwise.
347+
line.reset(_state.cursor.graphicsRendition,
348+
_state.cursor.hyperlink,
349+
crispy::BufferFragment {
350+
_terminal.currentPtyBuffer(),
351+
_chars.substr(0, charsToWrite),
352+
});
353+
advanceCursorAfterWrite(ColumnCount::cast_from(charsToWrite));
354+
}
355+
else
356+
{
357+
// Transforming _chars input from UTF-8 to UTF-32 even though right now it should only
358+
// be containing US-ASCII, but soon it'll be any arbitrary textual Unicode codepoints.
359+
auto utf8DecoderState = unicode::utf8_decoder_state {};
360+
for (char const ch: _chars)
361+
{
362+
auto const result = unicode::from_utf8(utf8DecoderState, static_cast<uint8_t>(ch));
363+
if (holds_alternative<unicode::Success>(result))
364+
writeText(get<unicode::Success>(result).value);
365+
else if (holds_alternative<unicode::Invalid>(result))
366+
writeText(U'\uFFFE'); // U+FFFE (Not a Character)
367+
}
368+
}
369+
// fmt::print("emplaceCharsIntoCurrentLine ({} cols, {} bytes): \"{}\"\n", cellCount, _chars.size(),
370+
// _chars);
349371
return charsToWrite;
350372
}
351373

0 commit comments

Comments
 (0)