mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 22:29:43 +00:00
Let marks be cleared by clear (and friends) (#15686)
Move scroll marks to `TextBuffer`, so they can be cleared by EraseInDisplay and EraseScrollback. Also removes the namespacing on them. ## References and Relevant Issues * see also #11000 and #15057 * Resize/Reflow _doesn't_ work yet and I'm not attempting this here. ## Validation Steps Performed * `cls` works * `Clear-Host` works * `clear` works * the "Clear buffer" action works * They work when there's marks above the current viewport, and clear the scrollback * they work if you clear multiple "pages" of output, then scroll back to where marks previously were * resizing doesn't totally destroy the marks Closes #15426
This commit is contained in:
@@ -2660,6 +2660,9 @@ try
|
||||
// Set size back to real size as it will be taking over the rendering duties.
|
||||
newCursor.SetSize(ulSize);
|
||||
|
||||
newBuffer._marks = oldBuffer._marks;
|
||||
newBuffer._trimMarksOutsideBuffer();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
@@ -2869,3 +2872,114 @@ PointTree TextBuffer::GetPatterns(const til::CoordType firstRow, const til::Coor
|
||||
PointTree result(std::move(intervals));
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<ScrollMark>& TextBuffer::GetMarks() const noexcept
|
||||
{
|
||||
return _marks;
|
||||
}
|
||||
|
||||
// Remove all marks between `start` & `end`, inclusive.
|
||||
void TextBuffer::ClearMarksInRange(
|
||||
const til::point start,
|
||||
const til::point end)
|
||||
{
|
||||
auto inRange = [&start, &end](const ScrollMark& m) {
|
||||
return (m.start >= start && m.start <= end) ||
|
||||
(m.end >= start && m.end <= end);
|
||||
};
|
||||
|
||||
_marks.erase(std::remove_if(_marks.begin(),
|
||||
_marks.end(),
|
||||
inRange),
|
||||
_marks.end());
|
||||
}
|
||||
void TextBuffer::ClearAllMarks() noexcept
|
||||
{
|
||||
_marks.clear();
|
||||
}
|
||||
|
||||
// Adjust all the marks in the y-direction by `delta`. Positive values move the
|
||||
// marks down (the positive y direction). Negative values move up. This will
|
||||
// trim marks that are no longer have a start in the bounds of the buffer
|
||||
void TextBuffer::ScrollMarks(const int delta)
|
||||
{
|
||||
for (auto& mark : _marks)
|
||||
{
|
||||
mark.start.y += delta;
|
||||
|
||||
// If the mark had sub-regions, then move those pointers too
|
||||
if (mark.commandEnd.has_value())
|
||||
{
|
||||
(*mark.commandEnd).y += delta;
|
||||
}
|
||||
if (mark.outputEnd.has_value())
|
||||
{
|
||||
(*mark.outputEnd).y += delta;
|
||||
}
|
||||
}
|
||||
_trimMarksOutsideBuffer();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Add a mark to our list of marks, and treat it as the active "prompt". For
|
||||
// the sake of shell integration, we need to know which mark represents the
|
||||
// current prompt/command/output. Internally, we'll always treat the _last_
|
||||
// mark in the list as the current prompt.
|
||||
// Arguments:
|
||||
// - m: the mark to add.
|
||||
void TextBuffer::StartPromptMark(const ScrollMark& m)
|
||||
{
|
||||
_marks.push_back(m);
|
||||
}
|
||||
// Method Description:
|
||||
// - Add a mark to our list of marks. Don't treat this as the active prompt.
|
||||
// This should be used for marks created by the UI or from other user input.
|
||||
// By inserting at the start of the list, we can separate out marks that were
|
||||
// generated by client programs vs ones created by the user.
|
||||
// Arguments:
|
||||
// - m: the mark to add.
|
||||
void TextBuffer::AddMark(const ScrollMark& m)
|
||||
{
|
||||
_marks.insert(_marks.begin(), m);
|
||||
}
|
||||
|
||||
void TextBuffer::_trimMarksOutsideBuffer()
|
||||
{
|
||||
const auto height = GetSize().Height();
|
||||
_marks.erase(std::remove_if(_marks.begin(),
|
||||
_marks.end(),
|
||||
[height](const auto& m) {
|
||||
return (m.start.y < 0) ||
|
||||
(m.start.y >= height);
|
||||
}),
|
||||
_marks.end());
|
||||
}
|
||||
|
||||
void TextBuffer::SetCurrentPromptEnd(const til::point pos) noexcept
|
||||
{
|
||||
if (_marks.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto& curr{ _marks.back() };
|
||||
curr.end = pos;
|
||||
}
|
||||
void TextBuffer::SetCurrentCommandEnd(const til::point pos) noexcept
|
||||
{
|
||||
if (_marks.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto& curr{ _marks.back() };
|
||||
curr.commandEnd = pos;
|
||||
}
|
||||
void TextBuffer::SetCurrentOutputEnd(const til::point pos, ::MarkCategory category) noexcept
|
||||
{
|
||||
if (_marks.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto& curr{ _marks.back() };
|
||||
curr.outputEnd = pos;
|
||||
curr.category = category;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,41 @@ namespace Microsoft::Console::Render
|
||||
class Renderer;
|
||||
}
|
||||
|
||||
enum class MarkCategory
|
||||
{
|
||||
Prompt = 0,
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Success = 3,
|
||||
Info = 4
|
||||
};
|
||||
struct ScrollMark
|
||||
{
|
||||
std::optional<til::color> color;
|
||||
til::point start;
|
||||
til::point end; // exclusive
|
||||
std::optional<til::point> commandEnd;
|
||||
std::optional<til::point> outputEnd;
|
||||
|
||||
MarkCategory category{ MarkCategory::Info };
|
||||
// Other things we may want to think about in the future are listed in
|
||||
// GH#11000
|
||||
|
||||
bool HasCommand() const noexcept
|
||||
{
|
||||
return commandEnd.has_value() && *commandEnd != end;
|
||||
}
|
||||
bool HasOutput() const noexcept
|
||||
{
|
||||
return outputEnd.has_value() && *outputEnd != *commandEnd;
|
||||
}
|
||||
std::pair<til::point, til::point> GetExtent() const
|
||||
{
|
||||
til::point realEnd{ til::coalesce_value(outputEnd, commandEnd, end) };
|
||||
return std::make_pair(til::point{ start }, realEnd);
|
||||
}
|
||||
};
|
||||
|
||||
class TextBuffer final
|
||||
{
|
||||
public:
|
||||
@@ -228,6 +263,16 @@ public:
|
||||
void CopyPatterns(const TextBuffer& OtherBuffer);
|
||||
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const til::CoordType firstRow, const til::CoordType lastRow) const;
|
||||
|
||||
const std::vector<ScrollMark>& GetMarks() const noexcept;
|
||||
void ClearMarksInRange(const til::point start, const til::point end);
|
||||
void ClearAllMarks() noexcept;
|
||||
void ScrollMarks(const int delta);
|
||||
void StartPromptMark(const ScrollMark& m);
|
||||
void AddMark(const ScrollMark& m);
|
||||
void SetCurrentPromptEnd(const til::point pos) noexcept;
|
||||
void SetCurrentCommandEnd(const til::point pos) noexcept;
|
||||
void SetCurrentOutputEnd(const til::point pos, ::MarkCategory category) noexcept;
|
||||
|
||||
private:
|
||||
void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes);
|
||||
void _commit(const std::byte* row);
|
||||
@@ -251,6 +296,7 @@ private:
|
||||
til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const;
|
||||
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
void _PruneHyperlinks();
|
||||
void _trimMarksOutsideBuffer();
|
||||
|
||||
static void _AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text);
|
||||
|
||||
@@ -327,6 +373,8 @@ private:
|
||||
|
||||
bool _isActiveBuffer = false;
|
||||
|
||||
std::vector<ScrollMark> _marks;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
friend class UiaTextRangeTests;
|
||||
|
||||
@@ -2111,7 +2111,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlCore::AddMark(const Control::ScrollMark& mark)
|
||||
{
|
||||
::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark m{};
|
||||
::ScrollMark m{};
|
||||
|
||||
if (mark.Color.HasValue)
|
||||
{
|
||||
@@ -2140,7 +2140,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto currentOffset = ScrollOffset();
|
||||
const auto& marks{ _terminal->GetScrollMarks() };
|
||||
|
||||
std::optional<DispatchTypes::ScrollMark> tgt;
|
||||
std::optional<::ScrollMark> tgt;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
@@ -2243,7 +2243,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
|
||||
_terminal->GetTextBuffer().GetCursor().GetPosition();
|
||||
|
||||
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
|
||||
std::optional<::ScrollMark> nearest{ std::nullopt };
|
||||
const auto& marks{ _terminal->GetScrollMarks() };
|
||||
|
||||
// Early return so we don't have to check for the validity of `nearest` below after the loop exits.
|
||||
@@ -2283,7 +2283,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
|
||||
_terminal->GetTextBuffer().GetCursor().GetPosition();
|
||||
|
||||
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
|
||||
std::optional<::ScrollMark> nearest{ std::nullopt };
|
||||
const auto& marks{ _terminal->GetScrollMarks() };
|
||||
|
||||
static constexpr til::point worst{ til::CoordTypeMax, til::CoordTypeMax };
|
||||
@@ -2357,8 +2357,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlCore::_contextMenuSelectMark(
|
||||
const til::point& pos,
|
||||
bool (*filter)(const DispatchTypes::ScrollMark&),
|
||||
til::point_span (*getSpan)(const DispatchTypes::ScrollMark&))
|
||||
bool (*filter)(const ::ScrollMark&),
|
||||
til::point_span (*getSpan)(const ::ScrollMark&))
|
||||
{
|
||||
// Do nothing if the caller didn't give us a way to get the span to select for this mark.
|
||||
if (!getSpan)
|
||||
@@ -2391,20 +2391,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_contextMenuSelectMark(
|
||||
_contextMenuBufferPosition,
|
||||
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasCommand(); },
|
||||
[](const DispatchTypes::ScrollMark& m) { return til::point_span{ m.end, *m.commandEnd }; });
|
||||
[](const ::ScrollMark& m) -> bool { return !m.HasCommand(); },
|
||||
[](const ::ScrollMark& m) { return til::point_span{ m.end, *m.commandEnd }; });
|
||||
}
|
||||
void ControlCore::ContextMenuSelectOutput()
|
||||
{
|
||||
_contextMenuSelectMark(
|
||||
_contextMenuBufferPosition,
|
||||
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasOutput(); },
|
||||
[](const DispatchTypes::ScrollMark& m) { return til::point_span{ *m.commandEnd, *m.outputEnd }; });
|
||||
[](const ::ScrollMark& m) -> bool { return !m.HasOutput(); },
|
||||
[](const ::ScrollMark& m) { return til::point_span{ *m.commandEnd, *m.outputEnd }; });
|
||||
}
|
||||
|
||||
bool ControlCore::_clickedOnMark(
|
||||
const til::point& pos,
|
||||
bool (*filter)(const DispatchTypes::ScrollMark&))
|
||||
bool (*filter)(const ::ScrollMark&))
|
||||
{
|
||||
// Don't show this if the click was on the selection
|
||||
if (_terminal->IsSelectionActive() &&
|
||||
@@ -2442,7 +2442,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Relies on the anchor set in AnchorContextMenu
|
||||
return _clickedOnMark(_contextMenuBufferPosition,
|
||||
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasCommand(); });
|
||||
[](const ::ScrollMark& m) -> bool { return !m.HasCommand(); });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -2451,6 +2451,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Relies on the anchor set in AnchorContextMenu
|
||||
return _clickedOnMark(_contextMenuBufferPosition,
|
||||
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasOutput(); });
|
||||
[](const ::ScrollMark& m) -> bool { return !m.HasOutput(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,10 +372,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void _contextMenuSelectMark(
|
||||
const til::point& pos,
|
||||
bool (*filter)(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&),
|
||||
til::point_span (*getSpan)(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&));
|
||||
bool (*filter)(const ::ScrollMark&),
|
||||
til::point_span (*getSpan)(const ::ScrollMark&));
|
||||
|
||||
bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&));
|
||||
bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::ScrollMark&));
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
|
||||
@@ -1344,7 +1344,7 @@ void Terminal::_updateUrlDetection()
|
||||
}
|
||||
|
||||
// NOTE: This is the version of AddMark that comes from the UI. The VT api call into this too.
|
||||
void Terminal::AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark,
|
||||
void Terminal::AddMark(const ScrollMark& mark,
|
||||
const til::point& start,
|
||||
const til::point& end,
|
||||
const bool fromUi)
|
||||
@@ -1354,17 +1354,18 @@ void Terminal::AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes:
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchTypes::ScrollMark m = mark;
|
||||
ScrollMark m = mark;
|
||||
m.start = start;
|
||||
m.end = end;
|
||||
|
||||
// If the mark came from the user adding a mark via the UI, don't make it the active prompt mark.
|
||||
if (fromUi)
|
||||
{
|
||||
_scrollMarks.insert(_scrollMarks.begin(), m);
|
||||
_activeBuffer().AddMark(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
_scrollMarks.push_back(m);
|
||||
_activeBuffer().StartPromptMark(m);
|
||||
}
|
||||
|
||||
// Tell the control that the scrollbar has somehow changed. Used as a
|
||||
@@ -1389,15 +1390,7 @@ void Terminal::ClearMark()
|
||||
start = til::point{ GetSelectionAnchor() };
|
||||
end = til::point{ GetSelectionEnd() };
|
||||
}
|
||||
auto inSelection = [&start, &end](const DispatchTypes::ScrollMark& m) {
|
||||
return (m.start >= start && m.start <= end) ||
|
||||
(m.end >= start && m.end <= end);
|
||||
};
|
||||
|
||||
_scrollMarks.erase(std::remove_if(_scrollMarks.begin(),
|
||||
_scrollMarks.end(),
|
||||
inSelection),
|
||||
_scrollMarks.end());
|
||||
_activeBuffer().ClearMarksInRange(start, end);
|
||||
|
||||
// Tell the control that the scrollbar has somehow changed. Used as a
|
||||
// workaround to force the control to redraw any scrollbar marks
|
||||
@@ -1405,23 +1398,22 @@ void Terminal::ClearMark()
|
||||
}
|
||||
void Terminal::ClearAllMarks() noexcept
|
||||
{
|
||||
_scrollMarks.clear();
|
||||
_activeBuffer().ClearAllMarks();
|
||||
// Tell the control that the scrollbar has somehow changed. Used as a
|
||||
// workaround to force the control to redraw any scrollbar marks
|
||||
_NotifyScrollEvent();
|
||||
}
|
||||
|
||||
const std::vector<DispatchTypes::ScrollMark>& Terminal::GetScrollMarks() const noexcept
|
||||
const std::vector<ScrollMark>& Terminal::GetScrollMarks() const noexcept
|
||||
{
|
||||
// TODO: GH#11000 - when the marks are stored per-buffer, get rid of this.
|
||||
// We want to return _no_ marks when we're in the alt buffer, to effectively
|
||||
// hide them. We need to return a reference, so we can't just ctor an empty
|
||||
// list here just for when we're in the alt buffer.
|
||||
static const std::vector<DispatchTypes::ScrollMark> _altBufferMarks{};
|
||||
return _inAltBuffer() ? _altBufferMarks : _scrollMarks;
|
||||
return _activeBuffer().GetMarks();
|
||||
}
|
||||
|
||||
til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) const
|
||||
til::color Terminal::GetColorForMark(const ScrollMark& mark) const
|
||||
{
|
||||
if (mark.color.has_value())
|
||||
{
|
||||
@@ -1430,24 +1422,24 @@ til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal::
|
||||
|
||||
switch (mark.category)
|
||||
{
|
||||
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Prompt:
|
||||
case MarkCategory::Prompt:
|
||||
{
|
||||
return _renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
|
||||
}
|
||||
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Error:
|
||||
case MarkCategory::Error:
|
||||
{
|
||||
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED);
|
||||
}
|
||||
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Warning:
|
||||
case MarkCategory::Warning:
|
||||
{
|
||||
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_YELLOW);
|
||||
}
|
||||
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Success:
|
||||
case MarkCategory::Success:
|
||||
{
|
||||
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN);
|
||||
}
|
||||
default:
|
||||
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Info:
|
||||
case MarkCategory::Info:
|
||||
{
|
||||
return _renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ public:
|
||||
RenderSettings& GetRenderSettings() noexcept { return _renderSettings; };
|
||||
const RenderSettings& GetRenderSettings() const noexcept { return _renderSettings; };
|
||||
|
||||
const std::vector<Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark>& GetScrollMarks() const noexcept;
|
||||
void AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark,
|
||||
const std::vector<ScrollMark>& GetScrollMarks() const noexcept;
|
||||
void AddMark(const ScrollMark& mark,
|
||||
const til::point& start,
|
||||
const til::point& end,
|
||||
const bool fromUi);
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
void UseAlternateScreenBuffer(const TextAttribute& attrs) override;
|
||||
void UseMainScreenBuffer() override;
|
||||
|
||||
void MarkPrompt(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) override;
|
||||
void MarkPrompt(const ScrollMark& mark) override;
|
||||
void MarkCommandStart() override;
|
||||
void MarkOutputStart() override;
|
||||
void MarkCommandFinish(std::optional<unsigned int> error) override;
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
|
||||
void ClearMark();
|
||||
void ClearAllMarks() noexcept;
|
||||
til::color GetColorForMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) const;
|
||||
til::color GetColorForMark(const ScrollMark& mark) const;
|
||||
|
||||
#pragma region ITerminalInput
|
||||
// These methods are defined in Terminal.cpp
|
||||
@@ -393,7 +393,6 @@ private:
|
||||
};
|
||||
std::optional<KeyEventCodes> _lastKeyEventCodes;
|
||||
|
||||
std::vector<Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark> _scrollMarks;
|
||||
enum class PromptState : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
|
||||
@@ -291,7 +291,7 @@ void Terminal::UseMainScreenBuffer()
|
||||
}
|
||||
|
||||
// NOTE: This is the version of AddMark that comes from VT
|
||||
void Terminal::MarkPrompt(const DispatchTypes::ScrollMark& mark)
|
||||
void Terminal::MarkPrompt(const ScrollMark& mark)
|
||||
{
|
||||
static bool logged = false;
|
||||
if (!logged)
|
||||
@@ -309,7 +309,7 @@ void Terminal::MarkPrompt(const DispatchTypes::ScrollMark& mark)
|
||||
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
|
||||
AddMark(mark, cursorPos, cursorPos, false);
|
||||
|
||||
if (mark.category == DispatchTypes::MarkCategory::Prompt)
|
||||
if (mark.category == MarkCategory::Prompt)
|
||||
{
|
||||
_currentPromptState = PromptState::Prompt;
|
||||
}
|
||||
@@ -320,7 +320,7 @@ void Terminal::MarkCommandStart()
|
||||
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
|
||||
|
||||
if ((_currentPromptState == PromptState::Prompt) &&
|
||||
(_scrollMarks.size() > 0))
|
||||
(_activeBuffer().GetMarks().size() > 0))
|
||||
{
|
||||
// We were in the right state, and there's a previous mark to work
|
||||
// with.
|
||||
@@ -333,11 +333,11 @@ void Terminal::MarkCommandStart()
|
||||
// then abandon the current state, and just insert a new Prompt mark that
|
||||
// start's & ends here, and got to State::Command.
|
||||
|
||||
DispatchTypes::ScrollMark mark;
|
||||
mark.category = DispatchTypes::MarkCategory::Prompt;
|
||||
ScrollMark mark;
|
||||
mark.category = MarkCategory::Prompt;
|
||||
AddMark(mark, cursorPos, cursorPos, false);
|
||||
}
|
||||
_scrollMarks.back().end = cursorPos;
|
||||
_activeBuffer().SetCurrentPromptEnd(cursorPos);
|
||||
_currentPromptState = PromptState::Command;
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ void Terminal::MarkOutputStart()
|
||||
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
|
||||
|
||||
if ((_currentPromptState == PromptState::Command) &&
|
||||
(_scrollMarks.size() > 0))
|
||||
(_activeBuffer().GetMarks().size() > 0))
|
||||
{
|
||||
// We were in the right state, and there's a previous mark to work
|
||||
// with.
|
||||
@@ -359,27 +359,27 @@ void Terminal::MarkOutputStart()
|
||||
// then abandon the current state, and just insert a new Prompt mark that
|
||||
// start's & ends here, and the command ends here, and go to State::Output.
|
||||
|
||||
DispatchTypes::ScrollMark mark;
|
||||
mark.category = DispatchTypes::MarkCategory::Prompt;
|
||||
ScrollMark mark;
|
||||
mark.category = MarkCategory::Prompt;
|
||||
AddMark(mark, cursorPos, cursorPos, false);
|
||||
}
|
||||
_scrollMarks.back().commandEnd = cursorPos;
|
||||
_activeBuffer().SetCurrentCommandEnd(cursorPos);
|
||||
_currentPromptState = PromptState::Output;
|
||||
}
|
||||
|
||||
void Terminal::MarkCommandFinish(std::optional<unsigned int> error)
|
||||
{
|
||||
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
|
||||
auto category = DispatchTypes::MarkCategory::Prompt;
|
||||
auto category = MarkCategory::Prompt;
|
||||
if (error.has_value())
|
||||
{
|
||||
category = *error == 0u ?
|
||||
DispatchTypes::MarkCategory::Success :
|
||||
DispatchTypes::MarkCategory::Error;
|
||||
MarkCategory::Success :
|
||||
MarkCategory::Error;
|
||||
}
|
||||
|
||||
if ((_currentPromptState == PromptState::Output) &&
|
||||
(_scrollMarks.size() > 0))
|
||||
(_activeBuffer().GetMarks().size() > 0))
|
||||
{
|
||||
// We were in the right state, and there's a previous mark to work
|
||||
// with.
|
||||
@@ -393,13 +393,12 @@ void Terminal::MarkCommandFinish(std::optional<unsigned int> error)
|
||||
// ends here, and the command ends here, AND the output ends here. and
|
||||
// go to State::Output.
|
||||
|
||||
DispatchTypes::ScrollMark mark;
|
||||
mark.category = DispatchTypes::MarkCategory::Prompt;
|
||||
ScrollMark mark;
|
||||
mark.category = MarkCategory::Prompt;
|
||||
AddMark(mark, cursorPos, cursorPos, false);
|
||||
_scrollMarks.back().commandEnd = cursorPos;
|
||||
_activeBuffer().SetCurrentCommandEnd(cursorPos);
|
||||
}
|
||||
_scrollMarks.back().outputEnd = cursorPos;
|
||||
_scrollMarks.back().category = category;
|
||||
_activeBuffer().SetCurrentOutputEnd(cursorPos, category);
|
||||
_currentPromptState = PromptState::None;
|
||||
}
|
||||
|
||||
@@ -458,29 +457,11 @@ void Terminal::NotifyBufferRotation(const int delta)
|
||||
// manually erase our pattern intervals since the locations have changed now
|
||||
_patternIntervalTree = {};
|
||||
|
||||
const auto hasScrollMarks = _scrollMarks.size() > 0;
|
||||
auto& marks{ _activeBuffer().GetMarks() };
|
||||
const auto hasScrollMarks = marks.size() > 0;
|
||||
if (hasScrollMarks)
|
||||
{
|
||||
for (auto& mark : _scrollMarks)
|
||||
{
|
||||
// Move the mark up
|
||||
mark.start.y -= delta;
|
||||
|
||||
// If the mark had sub-regions, then move those pointers too
|
||||
if (mark.commandEnd.has_value())
|
||||
{
|
||||
(*mark.commandEnd).y -= delta;
|
||||
}
|
||||
if (mark.outputEnd.has_value())
|
||||
{
|
||||
(*mark.outputEnd).y -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
_scrollMarks.erase(std::remove_if(_scrollMarks.begin(),
|
||||
_scrollMarks.end(),
|
||||
[](const auto& m) { return m.start.y < 0; }),
|
||||
_scrollMarks.end());
|
||||
_activeBuffer().ScrollMarks(-delta);
|
||||
}
|
||||
|
||||
const auto oldScrollOffset = _scrollOffset;
|
||||
|
||||
@@ -435,7 +435,7 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta)
|
||||
}
|
||||
}
|
||||
|
||||
void ConhostInternalGetSet::MarkPrompt(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& /*mark*/)
|
||||
void ConhostInternalGetSet::MarkPrompt(const ::ScrollMark& /*mark*/)
|
||||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
void NotifyAccessibilityChange(const til::rect& changedRect) override;
|
||||
void NotifyBufferRotation(const int delta) override;
|
||||
|
||||
void MarkPrompt(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) override;
|
||||
void MarkPrompt(const ScrollMark& mark) override;
|
||||
void MarkCommandStart() override;
|
||||
void MarkOutputStart() override;
|
||||
void MarkCommandFinish(std::optional<unsigned int> error) override;
|
||||
|
||||
@@ -654,39 +654,4 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
||||
constexpr VTInt s_sDECCOLMSetColumns = 132;
|
||||
constexpr VTInt s_sDECCOLMResetColumns = 80;
|
||||
|
||||
enum class MarkCategory : size_t
|
||||
{
|
||||
Prompt = 0,
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Success = 3,
|
||||
Info = 4
|
||||
};
|
||||
|
||||
struct ScrollMark
|
||||
{
|
||||
std::optional<til::color> color;
|
||||
til::point start;
|
||||
til::point end; // exclusive
|
||||
std::optional<til::point> commandEnd;
|
||||
std::optional<til::point> outputEnd;
|
||||
|
||||
MarkCategory category{ MarkCategory::Info };
|
||||
// Other things we may want to think about in the future are listed in
|
||||
// GH#11000
|
||||
|
||||
bool HasCommand() const noexcept
|
||||
{
|
||||
return commandEnd.has_value() && *commandEnd != end;
|
||||
}
|
||||
bool HasOutput() const noexcept
|
||||
{
|
||||
return outputEnd.has_value() && *outputEnd != *commandEnd;
|
||||
}
|
||||
std::pair<til::point, til::point> GetExtent() const
|
||||
{
|
||||
til::point realEnd{ til::coalesce_value(outputEnd, commandEnd, end) };
|
||||
return std::make_pair(til::point{ start }, realEnd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0;
|
||||
virtual void NotifyBufferRotation(const int delta) = 0;
|
||||
|
||||
virtual void MarkPrompt(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) = 0;
|
||||
virtual void MarkPrompt(const ScrollMark& mark) = 0;
|
||||
virtual void MarkCommandStart() = 0;
|
||||
virtual void MarkOutputStart() = 0;
|
||||
virtual void MarkCommandFinish(std::optional<unsigned int> error) = 0;
|
||||
|
||||
@@ -3145,6 +3145,12 @@ bool AdaptDispatch::_EraseScrollback()
|
||||
auto& cursor = textBuffer.GetCursor();
|
||||
const auto row = cursor.GetPosition().y;
|
||||
|
||||
// Clear all the marks below the new viewport position.
|
||||
textBuffer.ClearMarksInRange(til::point{ 0, height },
|
||||
til::point{ bufferSize.width, bufferSize.height });
|
||||
// Then scroll all the remaining marks up. This will trim ones that are now "outside" the buffer
|
||||
textBuffer.ScrollMarks(-top);
|
||||
|
||||
// Scroll the viewport content to the top of the buffer.
|
||||
textBuffer.ScrollRows(top, height, -top);
|
||||
// Clear everything after the viewport.
|
||||
@@ -3227,6 +3233,10 @@ bool AdaptDispatch::_EraseAll()
|
||||
// Also reset the line rendition for the erased rows.
|
||||
textBuffer.ResetLineRenditionRange(newViewportTop, newViewportBottom);
|
||||
|
||||
// Clear any marks that remain below the start of the
|
||||
textBuffer.ClearMarksInRange(til::point{ 0, newViewportTop },
|
||||
til::point{ bufferSize.Width(), bufferSize.Height() });
|
||||
|
||||
// GH#5683 - If this succeeded, but we're in a conpty, return `false` to
|
||||
// make the state machine propagate this ED sequence to the connected
|
||||
// terminal application. While we're in conpty mode, when the client
|
||||
@@ -3633,8 +3643,8 @@ bool AdaptDispatch::DoITerm2Action(const std::wstring_view string)
|
||||
|
||||
if (action == L"SetMark")
|
||||
{
|
||||
DispatchTypes::ScrollMark mark;
|
||||
mark.category = DispatchTypes::MarkCategory::Prompt;
|
||||
ScrollMark mark;
|
||||
mark.category = MarkCategory::Prompt;
|
||||
_api.MarkPrompt(mark);
|
||||
return true;
|
||||
}
|
||||
@@ -3681,8 +3691,8 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
|
||||
case L'A': // FTCS_PROMPT
|
||||
{
|
||||
// Simply just mark this line as a prompt line.
|
||||
DispatchTypes::ScrollMark mark;
|
||||
mark.category = DispatchTypes::MarkCategory::Prompt;
|
||||
ScrollMark mark;
|
||||
mark.category = MarkCategory::Prompt;
|
||||
_api.MarkPrompt(mark);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
Log::Comment(L"NotifyBufferRotation MOCK called...");
|
||||
}
|
||||
|
||||
void MarkPrompt(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& /*mark*/) override
|
||||
void MarkPrompt(const ScrollMark& /*mark*/) override
|
||||
{
|
||||
Log::Comment(L"MarkPrompt MOCK called...");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user