Repository: microsoft/terminal
File: src/cascadia/TerminalControl/ControlCore.cpp (C++)

Code before:
    void ControlCore::AnchorContextMenu(const til::point viewportRelativeCharacterPosition)
    {
        // viewportRelativeCharacterPosition is relative to the current
        // viewport, so adjust for that:
        _contextMenuBufferPosition = _terminal->GetViewport().Origin() + viewportRelativeCharacterPosition;
    }

    void ControlCore::ContextMenuSelectCommand()
    {
        const auto& marks{ _terminal->GetScrollMarks() };
        for (auto&& m : marks)
        {
            if (!m.HasCommand())
            {
                continue;
            }
            // If they clicked _anywhere_ in the mark...
            const auto [markStart, markEnd] = m.GetExtent();
            if (markStart <= _contextMenuBufferPosition &&
                markEnd >= _contextMenuBufferPosition)
            {
                // Select the command
                const auto start = m.end;
                auto end = *m.commandEnd;

                const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
                bufferSize.DecrementInBounds(end);

                auto lock = _terminal->LockForWriting();
                _terminal->SelectNewRegion(start, end);
                _renderer->TriggerSelection();
                return;
            }
        }
    }
    void ControlCore::ContextMenuSelectOutput()
    {
        const auto& marks{ _terminal->GetScrollMarks() };
        for (auto&& m : marks)
        {
            if (!m.HasOutput())
            {
                continue;
            }
            // If they clicked _anywhere_ in the mark...
            const auto [markStart, markEnd] = m.GetExtent();
            if (markStart <= _contextMenuBufferPosition &&
                markEnd >= _contextMenuBufferPosition)
            {
                // Select the output
                const auto start = *m.commandEnd;

Review comment (bug):
I was alright with two copies of the same-looking code, but I am starting to get a bit worried about there being six. Are they different enough that introducing a single helper would make the code less clear?

I'm imagining...

```
enum Position {
Enclosing,
Prior,
Following,
} ;

std::optional<std::pair<coord, coord>> GetExtentForMarkAtPosition(Type t /* well, HasCommand isn't a type exactly... hmm */, Position pos, coord anchor);
```

since we are using it for...

1. is present at coord
2. get at coord
3. get before coord
4. get after coord

over the types...

1. has command
2. has output

with...

1. different anchor points
2. no anchor (???) bottom of the viewport, or the cursor, or whatever

@lhecker is this the wrong kind of deduplication?

Code after:
        const til::point& pos,
        const std::function<bool(const DispatchTypes::ScrollMark&)>& filter,
        const std::function<til::point_span(const DispatchTypes::ScrollMark&)>& getSpan)
    {
        // Do nothing if the caller didn't give us a way to get the span to select for this mark.
        if (!getSpan)
        {
            return;
        }
        const auto& marks{ _terminal->GetScrollMarks() };
        for (auto&& m : marks)
        {
            // If the caller gave us a way to filter marks, check that now.
            // This can be used to filter to only marks that have a command, or output.
            if (filter && filter(m))
            {
                continue;
            }
            // If they clicked _anywhere_ in the mark...
            const auto [markStart, markEnd] = m.GetExtent();
            if (markStart <= pos &&
                markEnd >= pos)
            {
                // ... select the part of the mark the caller told us about.
                _selectSpan(getSpan(m));
                // And quick bail
                return;
            }
        }
    }

    void ControlCore::ContextMenuSelectCommand()
    {
        _contextMenuSelectMark(
            _contextMenuBufferPosition,
            [](const DispatchTypes::ScrollMark& m) -> bool { return !m.HasCommand(); },
            [](const DispatchTypes::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 }; });
    }

    bool ControlCore::_clickedOnMark(
        const til::point& pos,
        const std::function<bool(const DispatchTypes::ScrollMark&)>& filter)
    {
        // Don't show this if the click was on the selection