Skip to content

Commit b4042ea

Browse files
authored
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
1 parent 482c724 commit b4042ea

File tree

13 files changed

+236
-127
lines changed

13 files changed

+236
-127
lines changed

src/buffer/out/textBuffer.cpp

+114
Original file line numberDiff line numberDiff line change
@@ -2660,6 +2660,9 @@ try
26602660
// Set size back to real size as it will be taking over the rendering duties.
26612661
newCursor.SetSize(ulSize);
26622662

2663+
newBuffer._marks = oldBuffer._marks;
2664+
newBuffer._trimMarksOutsideBuffer();
2665+
26632666
return S_OK;
26642667
}
26652668
CATCH_RETURN()
@@ -2869,3 +2872,114 @@ PointTree TextBuffer::GetPatterns(const til::CoordType firstRow, const til::Coor
28692872
PointTree result(std::move(intervals));
28702873
return result;
28712874
}
2875+
2876+
const std::vector<ScrollMark>& TextBuffer::GetMarks() const noexcept
2877+
{
2878+
return _marks;
2879+
}
2880+
2881+
// Remove all marks between `start` & `end`, inclusive.
2882+
void TextBuffer::ClearMarksInRange(
2883+
const til::point start,
2884+
const til::point end)
2885+
{
2886+
auto inRange = [&start, &end](const ScrollMark& m) {
2887+
return (m.start >= start && m.start <= end) ||
2888+
(m.end >= start && m.end <= end);
2889+
};
2890+
2891+
_marks.erase(std::remove_if(_marks.begin(),
2892+
_marks.end(),
2893+
inRange),
2894+
_marks.end());
2895+
}
2896+
void TextBuffer::ClearAllMarks() noexcept
2897+
{
2898+
_marks.clear();
2899+
}
2900+
2901+
// Adjust all the marks in the y-direction by `delta`. Positive values move the
2902+
// marks down (the positive y direction). Negative values move up. This will
2903+
// trim marks that are no longer have a start in the bounds of the buffer
2904+
void TextBuffer::ScrollMarks(const int delta)
2905+
{
2906+
for (auto& mark : _marks)
2907+
{
2908+
mark.start.y += delta;
2909+
2910+
// If the mark had sub-regions, then move those pointers too
2911+
if (mark.commandEnd.has_value())
2912+
{
2913+
(*mark.commandEnd).y += delta;
2914+
}
2915+
if (mark.outputEnd.has_value())
2916+
{
2917+
(*mark.outputEnd).y += delta;
2918+
}
2919+
}
2920+
_trimMarksOutsideBuffer();
2921+
}
2922+
2923+
// Method Description:
2924+
// - Add a mark to our list of marks, and treat it as the active "prompt". For
2925+
// the sake of shell integration, we need to know which mark represents the
2926+
// current prompt/command/output. Internally, we'll always treat the _last_
2927+
// mark in the list as the current prompt.
2928+
// Arguments:
2929+
// - m: the mark to add.
2930+
void TextBuffer::StartPromptMark(const ScrollMark& m)
2931+
{
2932+
_marks.push_back(m);
2933+
}
2934+
// Method Description:
2935+
// - Add a mark to our list of marks. Don't treat this as the active prompt.
2936+
// This should be used for marks created by the UI or from other user input.
2937+
// By inserting at the start of the list, we can separate out marks that were
2938+
// generated by client programs vs ones created by the user.
2939+
// Arguments:
2940+
// - m: the mark to add.
2941+
void TextBuffer::AddMark(const ScrollMark& m)
2942+
{
2943+
_marks.insert(_marks.begin(), m);
2944+
}
2945+
2946+
void TextBuffer::_trimMarksOutsideBuffer()
2947+
{
2948+
const auto height = GetSize().Height();
2949+
_marks.erase(std::remove_if(_marks.begin(),
2950+
_marks.end(),
2951+
[height](const auto& m) {
2952+
return (m.start.y < 0) ||
2953+
(m.start.y >= height);
2954+
}),
2955+
_marks.end());
2956+
}
2957+
2958+
void TextBuffer::SetCurrentPromptEnd(const til::point pos) noexcept
2959+
{
2960+
if (_marks.empty())
2961+
{
2962+
return;
2963+
}
2964+
auto& curr{ _marks.back() };
2965+
curr.end = pos;
2966+
}
2967+
void TextBuffer::SetCurrentCommandEnd(const til::point pos) noexcept
2968+
{
2969+
if (_marks.empty())
2970+
{
2971+
return;
2972+
}
2973+
auto& curr{ _marks.back() };
2974+
curr.commandEnd = pos;
2975+
}
2976+
void TextBuffer::SetCurrentOutputEnd(const til::point pos, ::MarkCategory category) noexcept
2977+
{
2978+
if (_marks.empty())
2979+
{
2980+
return;
2981+
}
2982+
auto& curr{ _marks.back() };
2983+
curr.outputEnd = pos;
2984+
curr.category = category;
2985+
}

src/buffer/out/textBuffer.hpp

+48
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,41 @@ namespace Microsoft::Console::Render
6464
class Renderer;
6565
}
6666

67+
enum class MarkCategory
68+
{
69+
Prompt = 0,
70+
Error = 1,
71+
Warning = 2,
72+
Success = 3,
73+
Info = 4
74+
};
75+
struct ScrollMark
76+
{
77+
std::optional<til::color> color;
78+
til::point start;
79+
til::point end; // exclusive
80+
std::optional<til::point> commandEnd;
81+
std::optional<til::point> outputEnd;
82+
83+
MarkCategory category{ MarkCategory::Info };
84+
// Other things we may want to think about in the future are listed in
85+
// GH#11000
86+
87+
bool HasCommand() const noexcept
88+
{
89+
return commandEnd.has_value() && *commandEnd != end;
90+
}
91+
bool HasOutput() const noexcept
92+
{
93+
return outputEnd.has_value() && *outputEnd != *commandEnd;
94+
}
95+
std::pair<til::point, til::point> GetExtent() const
96+
{
97+
til::point realEnd{ til::coalesce_value(outputEnd, commandEnd, end) };
98+
return std::make_pair(til::point{ start }, realEnd);
99+
}
100+
};
101+
67102
class TextBuffer final
68103
{
69104
public:
@@ -228,6 +263,16 @@ class TextBuffer final
228263
void CopyPatterns(const TextBuffer& OtherBuffer);
229264
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const til::CoordType firstRow, const til::CoordType lastRow) const;
230265

266+
const std::vector<ScrollMark>& GetMarks() const noexcept;
267+
void ClearMarksInRange(const til::point start, const til::point end);
268+
void ClearAllMarks() noexcept;
269+
void ScrollMarks(const int delta);
270+
void StartPromptMark(const ScrollMark& m);
271+
void AddMark(const ScrollMark& m);
272+
void SetCurrentPromptEnd(const til::point pos) noexcept;
273+
void SetCurrentCommandEnd(const til::point pos) noexcept;
274+
void SetCurrentOutputEnd(const til::point pos, ::MarkCategory category) noexcept;
275+
231276
private:
232277
void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes);
233278
void _commit(const std::byte* row);
@@ -251,6 +296,7 @@ class TextBuffer final
251296
til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const;
252297
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
253298
void _PruneHyperlinks();
299+
void _trimMarksOutsideBuffer();
254300

255301
static void _AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text);
256302

@@ -327,6 +373,8 @@ class TextBuffer final
327373

328374
bool _isActiveBuffer = false;
329375

376+
std::vector<ScrollMark> _marks;
377+
330378
#ifdef UNIT_TESTING
331379
friend class TextBufferTests;
332380
friend class UiaTextRangeTests;

src/cascadia/TerminalControl/ControlCore.cpp

+13-13
Original file line numberDiff line numberDiff line change
@@ -2111,7 +2111,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
21112111

21122112
void ControlCore::AddMark(const Control::ScrollMark& mark)
21132113
{
2114-
::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark m{};
2114+
::ScrollMark m{};
21152115

21162116
if (mark.Color.HasValue)
21172117
{
@@ -2140,7 +2140,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
21402140
const auto currentOffset = ScrollOffset();
21412141
const auto& marks{ _terminal->GetScrollMarks() };
21422142

2143-
std::optional<DispatchTypes::ScrollMark> tgt;
2143+
std::optional<::ScrollMark> tgt;
21442144

21452145
switch (direction)
21462146
{
@@ -2243,7 +2243,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
22432243
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
22442244
_terminal->GetTextBuffer().GetCursor().GetPosition();
22452245

2246-
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
2246+
std::optional<::ScrollMark> nearest{ std::nullopt };
22472247
const auto& marks{ _terminal->GetScrollMarks() };
22482248

22492249
// 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
22832283
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
22842284
_terminal->GetTextBuffer().GetCursor().GetPosition();
22852285

2286-
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
2286+
std::optional<::ScrollMark> nearest{ std::nullopt };
22872287
const auto& marks{ _terminal->GetScrollMarks() };
22882288

22892289
static constexpr til::point worst{ til::CoordTypeMax, til::CoordTypeMax };
@@ -2357,8 +2357,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
23572357

23582358
void ControlCore::_contextMenuSelectMark(
23592359
const til::point& pos,
2360-
bool (*filter)(const DispatchTypes::ScrollMark&),
2361-
til::point_span (*getSpan)(const DispatchTypes::ScrollMark&))
2360+
bool (*filter)(const ::ScrollMark&),
2361+
til::point_span (*getSpan)(const ::ScrollMark&))
23622362
{
23632363
// Do nothing if the caller didn't give us a way to get the span to select for this mark.
23642364
if (!getSpan)
@@ -2391,20 +2391,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
23912391
{
23922392
_contextMenuSelectMark(
23932393
_contextMenuBufferPosition,
2394-
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasCommand(); },
2395-
[](const DispatchTypes::ScrollMark& m) { return til::point_span{ m.end, *m.commandEnd }; });
2394+
[](const ::ScrollMark& m) -> bool { return !m.HasCommand(); },
2395+
[](const ::ScrollMark& m) { return til::point_span{ m.end, *m.commandEnd }; });
23962396
}
23972397
void ControlCore::ContextMenuSelectOutput()
23982398
{
23992399
_contextMenuSelectMark(
24002400
_contextMenuBufferPosition,
2401-
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasOutput(); },
2402-
[](const DispatchTypes::ScrollMark& m) { return til::point_span{ *m.commandEnd, *m.outputEnd }; });
2401+
[](const ::ScrollMark& m) -> bool { return !m.HasOutput(); },
2402+
[](const ::ScrollMark& m) { return til::point_span{ *m.commandEnd, *m.outputEnd }; });
24032403
}
24042404

24052405
bool ControlCore::_clickedOnMark(
24062406
const til::point& pos,
2407-
bool (*filter)(const DispatchTypes::ScrollMark&))
2407+
bool (*filter)(const ::ScrollMark&))
24082408
{
24092409
// Don't show this if the click was on the selection
24102410
if (_terminal->IsSelectionActive() &&
@@ -2442,7 +2442,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
24422442
{
24432443
// Relies on the anchor set in AnchorContextMenu
24442444
return _clickedOnMark(_contextMenuBufferPosition,
2445-
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasCommand(); });
2445+
[](const ::ScrollMark& m) -> bool { return !m.HasCommand(); });
24462446
}
24472447

24482448
// Method Description:
@@ -2451,6 +2451,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
24512451
{
24522452
// Relies on the anchor set in AnchorContextMenu
24532453
return _clickedOnMark(_contextMenuBufferPosition,
2454-
[](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasOutput(); });
2454+
[](const ::ScrollMark& m) -> bool { return !m.HasOutput(); });
24552455
}
24562456
}

src/cascadia/TerminalControl/ControlCore.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
372372

373373
void _contextMenuSelectMark(
374374
const til::point& pos,
375-
bool (*filter)(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&),
376-
til::point_span (*getSpan)(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&));
375+
bool (*filter)(const ::ScrollMark&),
376+
til::point_span (*getSpan)(const ::ScrollMark&));
377377

378-
bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&));
378+
bool _clickedOnMark(const til::point& pos, bool (*filter)(const ::ScrollMark&));
379379

380380
inline bool _IsClosing() const noexcept
381381
{

0 commit comments

Comments
 (0)