Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add text based cursor movement helpers #15779

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/buffer/out/textBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,20 @@ void TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
}
}

void TextBuffer::ConsumeGrapheme(std::wstring_view& chars) noexcept
// Given the character offset `position` in the `chars` string, this function returns the starting position of the next grapheme.
// For instance, given a `chars` of L"x\uD83D\uDE42y" and a `position` of 1 it'll return 3.
// GraphemePrev would do the exact inverse of this operation.
// In the future, these functions are expected to also deliver information about how many columns a grapheme occupies.
// (I know that mere UTF-16 code point iteration doesn't handle graphemes, but that's what we're working towards.)
size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position) noexcept
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, what is position. A column or a string index?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good point. I forgot to add function documentation. 🙂
(It's a string index, since it's for text-based navigation.)

{
// This function is supposed to mirror the behavior of ROW::Write, when it reads characters off of `chars`.
// (I know that a UTF-16 code point is not a grapheme, but that's what we're working towards.)
chars = til::utf16_pop(chars);
return til::utf16_iterate_next(chars, position);
}

// It's the counterpart to GraphemeNext. See GraphemeNext.
size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position) noexcept
{
return til::utf16_iterate_prev(chars, position);
}

// This function is intended for writing regular "lines" of text as it'll set the wrap flag on the given row.
Expand Down
4 changes: 3 additions & 1 deletion src/buffer/out/textBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ class TextBuffer final
TextBufferTextIterator GetTextLineDataAt(const til::point at) const;
TextBufferTextIterator GetTextDataAt(const til::point at, const Microsoft::Console::Types::Viewport limit) const;

static size_t GraphemeNext(const std::wstring_view& chars, size_t position) noexcept;
static size_t GraphemePrev(const std::wstring_view& chars, size_t position) noexcept;

// Text insertion functions
static void ConsumeGrapheme(std::wstring_view& chars) noexcept;
void Write(til::CoordType row, const TextAttribute& attributes, RowWriteState& state);
void FillRect(const til::rect& rect, const std::wstring_view& fill, const TextAttribute& attributes);

Expand Down
34 changes: 18 additions & 16 deletions src/inc/til/unicode.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,30 @@ namespace til
return { ptr, len };
}

// Removes the first code point off of `wstr` and returns the rest.
constexpr std::wstring_view utf16_pop(std::wstring_view wstr) noexcept
// Returns the index of the next codepoint in the given wstr (i.e. after the codepoint that idx points at).
constexpr size_t utf16_iterate_next(const std::wstring_view& wstr, size_t idx) noexcept
{
auto it = wstr.begin();
const auto end = wstr.end();

if (it != end)
if (idx < wstr.size() && is_leading_surrogate(til::at(wstr, idx++)))
{
const auto wch = *it;
++it;

if (is_surrogate(wch))
if (idx < wstr.size() && is_trailing_surrogate(til::at(wstr, idx)))
{
const auto wch2 = it != end ? *it : wchar_t{};
if (is_leading_surrogate(wch) && is_trailing_surrogate(wch2))
{
++it;
}
++idx;
}
}
return idx;
}

return { it, end };
// Returns the index of the preceding codepoint in the given wstr (i.e. in front of the codepoint that idx points at).
constexpr size_t utf16_iterate_prev(const std::wstring_view& wstr, size_t idx) noexcept
{
if (idx > 0 && is_trailing_surrogate(til::at(wstr, --idx)))
{
if (idx > 0 && is_leading_surrogate(til::at(wstr, idx - 1)))
{
--idx;
}
}
return idx;
}

// Splits a UTF16 string into codepoints, yielding `wstring_view`s of UTF16 text. Use it as:
Expand Down
2 changes: 1 addition & 1 deletion src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string)
// we tried writing a wide glyph into the last column which can't work.
if (textPositionBefore == textPositionAfter && (state.columnBegin == 0 || !wrapAtEOL))
{
textBuffer.ConsumeGrapheme(state.text);
state.text = state.text.substr(textBuffer.GraphemeNext(state.text, 0));
}

if (wrapAtEOL)
Expand Down
Loading