mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 14:19:45 +00:00
Compare commits
15 Commits
release-1.
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c21f74029d | ||
|
|
3719bad9ab | ||
|
|
bbb08b4478 | ||
|
|
7f341a25a5 | ||
|
|
550a685879 | ||
|
|
94f4db32cd | ||
|
|
36c41f06a4 | ||
|
|
abf3ba8207 | ||
|
|
77eb3ba037 | ||
|
|
8f9f3a9e7c | ||
|
|
416be4656f | ||
|
|
aae6ce60a4 | ||
|
|
1a2654d291 | ||
|
|
9aec69467c | ||
|
|
b5c8c854cc |
@@ -431,27 +431,6 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
|
||||
{
|
||||
proposedCursorPosition.Y++;
|
||||
}
|
||||
else if (wch == UNICODE_CARRIAGERETURN)
|
||||
{
|
||||
proposedCursorPosition.X = 0;
|
||||
}
|
||||
else if (wch == UNICODE_BACKSPACE)
|
||||
{
|
||||
if (cursorPosBefore.X == 0)
|
||||
{
|
||||
proposedCursorPosition.X = bufferSize.Width() - 1;
|
||||
proposedCursorPosition.Y--;
|
||||
}
|
||||
else
|
||||
{
|
||||
proposedCursorPosition.X--;
|
||||
}
|
||||
}
|
||||
else if (wch == UNICODE_BEL)
|
||||
{
|
||||
// TODO: GitHub #1883
|
||||
// For now its empty just so we don't try to write the BEL character
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: MSFT 21006766
|
||||
@@ -483,6 +462,13 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
|
||||
// With well behaving shells during normal operation this safeguard should normally not be encountered.
|
||||
proposedCursorPosition.X = 0;
|
||||
proposedCursorPosition.Y++;
|
||||
|
||||
// Try the character again.
|
||||
// TODO/DANGER: Does this actually work? The above comment seems
|
||||
// to mention the i-=1, but there was no such line here. This is
|
||||
// _scary_. Perhaps this was never hit before we had conpty
|
||||
// wrapping?
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ try
|
||||
Execute(L'\n');
|
||||
return true;
|
||||
case DispatchTypes::LineFeedType::WithReturn:
|
||||
Execute(L'\r');
|
||||
CarriageReturn();
|
||||
Execute(L'\n');
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -330,59 +330,12 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
||||
// Only act on a delayed EOL if we didn't move the cursor to a different position from where the EOL was marked.
|
||||
if (coordDelayedAt.X == CursorPosition.X && coordDelayedAt.Y == CursorPosition.Y)
|
||||
{
|
||||
bool fDoEolWrap = false;
|
||||
CursorPosition.X = 0;
|
||||
CursorPosition.Y++;
|
||||
|
||||
if (WI_IsFlagSet(dwFlags, WC_DELAY_EOL_WRAP))
|
||||
{
|
||||
// Correct if it's a printable character and whoever called us still understands/wants delayed EOL wrap.
|
||||
if (*lpString >= UNICODE_SPACE)
|
||||
{
|
||||
fDoEolWrap = true;
|
||||
}
|
||||
else if (*lpString == UNICODE_BACKSPACE)
|
||||
{
|
||||
// if we have an active wrap and a backspace comes in, process it by moving the cursor
|
||||
// back one cell position unless it's already at the start of a row.
|
||||
*pcb += sizeof(WCHAR);
|
||||
lpString++;
|
||||
pwchRealUnicode++;
|
||||
if (CursorPosition.X != 0)
|
||||
{
|
||||
--CursorPosition.X;
|
||||
Status = AdjustCursorPosition(screenInfo, CursorPosition, WI_IsFlagSet(dwFlags, WC_KEEP_CURSOR_VISIBLE), psScrollY);
|
||||
CursorPosition = cursor.GetPosition();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Uh oh, we've hit a consumer that doesn't know about delayed end of lines. To rectify this, just quickly jump
|
||||
// forward to the next line as if we had done it earlier, then let everything else play out normally.
|
||||
fDoEolWrap = true;
|
||||
}
|
||||
Status = AdjustCursorPosition(screenInfo, CursorPosition, WI_IsFlagSet(dwFlags, WC_KEEP_CURSOR_VISIBLE), psScrollY);
|
||||
|
||||
if (fDoEolWrap)
|
||||
{
|
||||
CursorPosition.X = 0;
|
||||
CursorPosition.Y++;
|
||||
|
||||
Status = AdjustCursorPosition(screenInfo, CursorPosition, WI_IsFlagSet(dwFlags, WC_KEEP_CURSOR_VISIBLE), psScrollY);
|
||||
|
||||
CursorPosition = cursor.GetPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (screenInfo.InVTMode())
|
||||
{
|
||||
// if we're at the beginning of a row and we get a backspace and told to limit backspacing, skip it
|
||||
if (*lpString == UNICODE_BACKSPACE && CursorPosition.X == 0 && WI_IsFlagSet(dwFlags, WC_LIMIT_BACKSPACE))
|
||||
{
|
||||
*pcb += sizeof(wchar_t);
|
||||
++lpString;
|
||||
++pwchRealUnicode;
|
||||
continue;
|
||||
CursorPosition = cursor.GetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,28 +402,23 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
||||
goto EndWhile;
|
||||
break;
|
||||
case UNICODE_TAB:
|
||||
if (screenInfo.InVTMode())
|
||||
{
|
||||
const ULONG TabSize = NUMBER_OF_SPACES_IN_TAB(XPosition);
|
||||
XPosition = (SHORT)(XPosition + TabSize);
|
||||
if (XPosition >= coordScreenBufferSize.X)
|
||||
{
|
||||
goto EndWhile;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ULONG TabSize = NUMBER_OF_SPACES_IN_TAB(XPosition);
|
||||
XPosition = (SHORT)(XPosition + TabSize);
|
||||
if (XPosition >= coordScreenBufferSize.X || WI_IsFlagSet(dwFlags, WC_NONDESTRUCTIVE_TAB))
|
||||
{
|
||||
goto EndWhile;
|
||||
}
|
||||
|
||||
for (ULONG j = 0; j < TabSize && i < LOCAL_BUFFER_SIZE; j++, i++)
|
||||
{
|
||||
*LocalBufPtr = UNICODE_SPACE;
|
||||
LocalBufPtr++;
|
||||
}
|
||||
for (ULONG j = 0; j < TabSize && i < LOCAL_BUFFER_SIZE; j++, i++)
|
||||
{
|
||||
*LocalBufPtr = UNICODE_SPACE;
|
||||
LocalBufPtr++;
|
||||
}
|
||||
|
||||
pwchBuffer++;
|
||||
break;
|
||||
}
|
||||
case UNICODE_LINEFEED:
|
||||
case UNICODE_CARRIAGERETURN:
|
||||
goto EndWhile;
|
||||
@@ -753,52 +701,40 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
||||
}
|
||||
case UNICODE_TAB:
|
||||
{
|
||||
// if VT-style tabs are set, then handle them the VT way, including not inserting spaces.
|
||||
// just move the cursor to the next tab stop.
|
||||
if (screenInfo.InVTMode())
|
||||
const size_t TabSize = NUMBER_OF_SPACES_IN_TAB(cursor.GetPosition().X);
|
||||
CursorPosition.X = (SHORT)(cursor.GetPosition().X + TabSize);
|
||||
|
||||
// move cursor forward to next tab stop. fill space with blanks.
|
||||
// we get here when the tab extends beyond the right edge of the
|
||||
// window. if the tab goes wraps the line, set the cursor to the first
|
||||
// position in the next line.
|
||||
pwchBuffer++;
|
||||
|
||||
TempNumSpaces += TabSize;
|
||||
size_t NumChars = 0;
|
||||
if (CursorPosition.X >= coordScreenBufferSize.X)
|
||||
{
|
||||
const COORD cCursorOld = cursor.GetPosition();
|
||||
CursorPosition = screenInfo.GetForwardTab(cCursorOld);
|
||||
NumChars = gsl::narrow<size_t>(coordScreenBufferSize.X - cursor.GetPosition().X);
|
||||
CursorPosition.X = 0;
|
||||
CursorPosition.Y = cursor.GetPosition().Y + 1;
|
||||
|
||||
// since you just tabbed yourself past the end of the row, set the wrap
|
||||
textBuffer.GetRowByOffset(cursor.GetPosition().Y).GetCharRow().SetWrapForced(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t TabSize = NUMBER_OF_SPACES_IN_TAB(cursor.GetPosition().X);
|
||||
CursorPosition.X = (SHORT)(cursor.GetPosition().X + TabSize);
|
||||
|
||||
// move cursor forward to next tab stop. fill space with blanks.
|
||||
// we get here when the tab extends beyond the right edge of the
|
||||
// window. if the tab goes wraps the line, set the cursor to the first
|
||||
// position in the next line.
|
||||
pwchBuffer++;
|
||||
|
||||
TempNumSpaces += TabSize;
|
||||
size_t NumChars = 0;
|
||||
if (CursorPosition.X >= coordScreenBufferSize.X)
|
||||
{
|
||||
NumChars = gsl::narrow<size_t>(coordScreenBufferSize.X - cursor.GetPosition().X);
|
||||
CursorPosition.X = 0;
|
||||
CursorPosition.Y = cursor.GetPosition().Y + 1;
|
||||
|
||||
// since you just tabbed yourself past the end of the row, set the wrap
|
||||
textBuffer.GetRowByOffset(cursor.GetPosition().Y).GetCharRow().SetWrapForced(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
NumChars = gsl::narrow<size_t>(CursorPosition.X - cursor.GetPosition().X);
|
||||
CursorPosition.Y = cursor.GetPosition().Y;
|
||||
}
|
||||
|
||||
if (!WI_IsFlagSet(dwFlags, WC_NONDESTRUCTIVE_TAB))
|
||||
{
|
||||
try
|
||||
{
|
||||
const OutputCellIterator it(UNICODE_SPACE, Attributes, NumChars);
|
||||
const auto done = screenInfo.Write(it, cursor.GetPosition());
|
||||
NumChars = done.GetCellDistance(it);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
NumChars = gsl::narrow<size_t>(CursorPosition.X - cursor.GetPosition().X);
|
||||
CursorPosition.Y = cursor.GetPosition().Y;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const OutputCellIterator it(UNICODE_SPACE, Attributes, NumChars);
|
||||
const auto done = screenInfo.Write(it, cursor.GetPosition());
|
||||
NumChars = done.GetCellDistance(it);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
Status = AdjustCursorPosition(screenInfo, CursorPosition, (dwFlags & WC_KEEP_CURSOR_VISIBLE) != 0, psScrollY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ void RedrawCommandLine(COOKED_READ_DATA& cookedReadData);
|
||||
//#define WC_FALSIFY_UNICODE 0x08
|
||||
|
||||
#define WC_LIMIT_BACKSPACE 0x10
|
||||
#define WC_NONDESTRUCTIVE_TAB 0x20
|
||||
//#define WC_NONDESTRUCTIVE_TAB 0x20 - This is not needed anymore, because the VT code handles tabs internally now.
|
||||
//#define WC_NEWLINE_SAVE_X 0x40 - This has been replaced with an output mode flag instead as it's line discipline behavior that may not necessarily be coupled with VT.
|
||||
#define WC_DELAY_EOL_WRAP 0x80
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ void WriteBuffer::_DefaultStringCase(const std::wstring_view string)
|
||||
&dwNumBytes,
|
||||
nullptr,
|
||||
_io.GetActiveOutputBuffer().GetTextBuffer().GetCursor().GetPosition().X,
|
||||
WC_LIMIT_BACKSPACE | WC_NONDESTRUCTIVE_TAB | WC_DELAY_EOL_WRAP,
|
||||
WC_LIMIT_BACKSPACE | WC_DELAY_EOL_WRAP,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ SCREEN_INFORMATION::~SCREEN_INFORMATION()
|
||||
|
||||
const NTSTATUS status = pScreen->_InitializeOutputStateMachine();
|
||||
|
||||
if (pScreen->InVTMode())
|
||||
if (pScreen->_IsInVTMode())
|
||||
{
|
||||
// microsoft/terminal#411: If VT mode is enabled, lets construct the
|
||||
// VT tab stops. Without this line, if a user has
|
||||
@@ -191,17 +191,6 @@ StateMachine& SCREEN_INFORMATION::GetStateMachine()
|
||||
return *_stateMachine;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - returns true if this buffer is in Virtual Terminal Output mode.
|
||||
// Arguments:
|
||||
// <none>
|
||||
// Return Value:
|
||||
// true iff this buffer is in Virtual Terminal Output mode.
|
||||
bool SCREEN_INFORMATION::InVTMode() const
|
||||
{
|
||||
return WI_IsFlagSet(OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This routine inserts the screen buffer pointer into the console's list of screen buffers.
|
||||
// Arguments:
|
||||
@@ -1964,6 +1953,17 @@ bool SCREEN_INFORMATION::_IsInPtyMode() const
|
||||
return _IsAltBuffer() || gci.IsInVtIoMode();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - returns true if this buffer is in Virtual Terminal Output mode.
|
||||
// Parameters:
|
||||
// - None
|
||||
// Return Value:
|
||||
// - true iff this buffer is in Virtual Terminal Output mode.
|
||||
bool SCREEN_INFORMATION::_IsInVTMode() const
|
||||
{
|
||||
return WI_IsFlagSet(OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets a VT tab stop in the column sColumn. If there is already a tab there, it does nothing.
|
||||
// Parameters:
|
||||
|
||||
@@ -113,8 +113,6 @@ public:
|
||||
bool SendNotifyBeep() const;
|
||||
bool PostUpdateWindowSize() const;
|
||||
|
||||
bool InVTMode() const;
|
||||
|
||||
// TODO: MSFT 9355062 these methods should probably be a part of construction/destruction. http://osgvsowi/9355062
|
||||
static void s_InsertScreenBuffer(_In_ SCREEN_INFORMATION* const pScreenInfo);
|
||||
static void s_RemoveScreenBuffer(_In_ SCREEN_INFORMATION* const pScreenInfo);
|
||||
@@ -273,6 +271,7 @@ private:
|
||||
|
||||
bool _IsAltBuffer() const;
|
||||
bool _IsInPtyMode() const;
|
||||
bool _IsInVTMode() const;
|
||||
|
||||
std::shared_ptr<Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
|
||||
_ColorTable(ColorTable),
|
||||
_cColorTable(cColorTable),
|
||||
_fUseAsciiOnly(fUseAsciiOnly),
|
||||
_previousLineWrapped(false),
|
||||
// _previousLineWrapped(false),
|
||||
_usingUnderLine(false),
|
||||
_needToDisableCursor(false),
|
||||
_lastCursorIsVisible(false),
|
||||
@@ -235,6 +235,8 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
_trace.TraceMoveCursor(coord);
|
||||
|
||||
if (coord.X != _lastText.X || coord.Y != _lastText.Y)
|
||||
{
|
||||
if (coord.X == 0 && coord.Y == 0)
|
||||
@@ -248,8 +250,15 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
|
||||
|
||||
// If the previous line wrapped, then the cursor is already at this
|
||||
// position, we just don't know it yet. Don't emit anything.
|
||||
if (_previousLineWrapped)
|
||||
bool previousLineWrapped = false;
|
||||
if (_wrappedRow.has_value())
|
||||
{
|
||||
previousLineWrapped = coord.Y == _wrappedRow.value() + 1;
|
||||
}
|
||||
|
||||
if (previousLineWrapped)
|
||||
{
|
||||
_trace.TraceWrapped();
|
||||
hr = S_OK;
|
||||
}
|
||||
else
|
||||
@@ -298,6 +307,9 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
|
||||
_newBottomLine = false;
|
||||
}
|
||||
_deferredCursorPos = INVALID_COORDS;
|
||||
|
||||
_wrappedRow = std::nullopt;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
@@ -376,8 +376,6 @@ using namespace Microsoft::Console::Types;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(_MoveCursor(coord));
|
||||
|
||||
std::wstring unclusteredString;
|
||||
unclusteredString.reserve(clusters.size());
|
||||
short totalWidth = 0;
|
||||
@@ -445,10 +443,37 @@ using namespace Microsoft::Console::Types;
|
||||
(totalWidth - numSpaces) :
|
||||
totalWidth;
|
||||
|
||||
if (cchActual == 0)
|
||||
{
|
||||
// If the previous row wrapped, but this line is empty, then we actually
|
||||
// do want to move the cursor down. Otherwise, we'll possibly end up
|
||||
// accidentally erasing the last character from the previous line, as
|
||||
// the cursor is still waiting on that character for the next character
|
||||
// to follow it.
|
||||
_wrappedRow = std::nullopt;
|
||||
// TODO:<Before PR>: Write a test that emulates ~/vttests/reflow-120.py
|
||||
// TODO:<Before PR>: Write a test that emulates ~/vttests/reflow-advanced.py
|
||||
}
|
||||
|
||||
// Move the cursor to the start of this run.
|
||||
RETURN_IF_FAILED(_MoveCursor(coord));
|
||||
|
||||
// Write the actual text string
|
||||
std::wstring wstr = std::wstring(unclusteredString.data(), cchActual);
|
||||
RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8(wstr));
|
||||
|
||||
// If we've written text to the last column of the viewport, then mark
|
||||
// that we've wrapped this line. The next time we attempt to move the
|
||||
// cursor, if we're trying to move it to the start of the next line,
|
||||
// we'll remember that this line was wrapped, and not manually break the
|
||||
// line.
|
||||
// Don't do this is the last character we're writing is a space - The last
|
||||
// char will always be a space, but if we see that, we shouldn't wrap.
|
||||
if ((_lastText.X + (totalWidth - numSpaces)) > _lastViewport.RightInclusive())
|
||||
{
|
||||
_wrappedRow = coord.Y;
|
||||
}
|
||||
|
||||
// Update our internal tracker of the cursor's position.
|
||||
// See MSFT:20266233
|
||||
// If the cursor is at the rightmost column of the terminal, and we write a
|
||||
|
||||
@@ -210,3 +210,30 @@ void RenderTracing::TraceLastText(const COORD lastTextPos) const
|
||||
UNREFERENCED_PARAMETER(lastTextPos);
|
||||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
void RenderTracing::TraceMoveCursor(const COORD pos) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
const auto lastTextStr = _CoordToString(pos);
|
||||
const auto cursor = lastTextStr.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceMoveCursor",
|
||||
TraceLoggingString(cursor),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
#else
|
||||
UNREFERENCED_PARAMETER(pos);
|
||||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
void RenderTracing::TraceWrapped() const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
const auto* const msg = "Wrapped instead of \\r\\n";
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceWrapped",
|
||||
TraceLoggingString(msg),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
#else
|
||||
UNREFERENCED_PARAMETER(pos);
|
||||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void TraceString(const std::string_view& str) const;
|
||||
void TraceInvalidate(const Microsoft::Console::Types::Viewport view) const;
|
||||
void TraceLastText(const COORD lastText) const;
|
||||
void TraceMoveCursor(const COORD pos) const;
|
||||
void TraceWrapped() const;
|
||||
void TraceInvalidateAll(const Microsoft::Console::Types::Viewport view) const;
|
||||
void TraceTriggerCircling(const bool newFrame) const;
|
||||
void TraceStartPaint(const bool quickReturn,
|
||||
|
||||
@@ -141,6 +141,8 @@ namespace Microsoft::Console::Render
|
||||
Microsoft::Console::VirtualTerminal::RenderTracing _trace;
|
||||
bool _inResizeRequest{ false };
|
||||
|
||||
std::optional<short> _wrappedRow{ std::nullopt };
|
||||
|
||||
[[nodiscard]] HRESULT _Write(std::string_view const str) noexcept;
|
||||
[[nodiscard]] HRESULT _WriteFormattedString(const std::string* const pFormat, ...) noexcept;
|
||||
[[nodiscard]] HRESULT _Flush() noexcept;
|
||||
|
||||
Reference in New Issue
Block a user