mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-28 19:59:17 +00:00
LibUnicode: Avoid rejecting end-of-text position as a valid boundary
When the cursor was positioned at the end of text, attempting to move it left(using the left arrow key) would fail because align_boundary() was rejecting the end-of-text position as a valid boundary.
This commit is contained in:
parent
0b23717bc9
commit
8ec72d6906
Notes:
github-actions[bot]
2025-04-11 19:31:19 +00:00
Author: https://github.com/mikiubo
Commit: 8ec72d6906
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4273
Reviewed-by: https://github.com/trflynn89 ✅
2 changed files with 38 additions and 18 deletions
|
@ -87,15 +87,13 @@ public:
|
||||||
virtual Optional<size_t> previous_boundary(size_t boundary, Inclusive inclusive) override
|
virtual Optional<size_t> previous_boundary(size_t boundary, Inclusive inclusive) override
|
||||||
{
|
{
|
||||||
auto icu_boundary = align_boundary(boundary);
|
auto icu_boundary = align_boundary(boundary);
|
||||||
if (!icu_boundary.has_value())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
if (inclusive == Inclusive::Yes) {
|
if (inclusive == Inclusive::Yes) {
|
||||||
if (static_cast<bool>(m_segmenter->isBoundary(*icu_boundary)))
|
if (static_cast<bool>(m_segmenter->isBoundary(icu_boundary)))
|
||||||
return static_cast<size_t>(*icu_boundary);
|
return static_cast<size_t>(icu_boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto index = m_segmenter->preceding(*icu_boundary); index != icu::BreakIterator::DONE)
|
if (auto index = m_segmenter->preceding(icu_boundary); index != icu::BreakIterator::DONE)
|
||||||
return static_cast<size_t>(index);
|
return static_cast<size_t>(index);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -104,15 +102,13 @@ public:
|
||||||
virtual Optional<size_t> next_boundary(size_t boundary, Inclusive inclusive) override
|
virtual Optional<size_t> next_boundary(size_t boundary, Inclusive inclusive) override
|
||||||
{
|
{
|
||||||
auto icu_boundary = align_boundary(boundary);
|
auto icu_boundary = align_boundary(boundary);
|
||||||
if (!icu_boundary.has_value())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
if (inclusive == Inclusive::Yes) {
|
if (inclusive == Inclusive::Yes) {
|
||||||
if (static_cast<bool>(m_segmenter->isBoundary(*icu_boundary)))
|
if (static_cast<bool>(m_segmenter->isBoundary(icu_boundary)))
|
||||||
return static_cast<size_t>(*icu_boundary);
|
return static_cast<size_t>(icu_boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto index = m_segmenter->following(*icu_boundary); index != icu::BreakIterator::DONE)
|
if (auto index = m_segmenter->following(icu_boundary); index != icu::BreakIterator::DONE)
|
||||||
return static_cast<size_t>(index);
|
return static_cast<size_t>(index);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -177,25 +173,25 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Optional<i32> align_boundary(size_t boundary)
|
i32 align_boundary(size_t boundary)
|
||||||
{
|
{
|
||||||
auto icu_boundary = static_cast<i32>(boundary);
|
auto icu_boundary = static_cast<i32>(boundary);
|
||||||
|
|
||||||
return m_segmented_text.visit(
|
return m_segmented_text.visit(
|
||||||
[&](String const& text) -> Optional<i32> {
|
[&](String const& text) {
|
||||||
if (boundary >= text.byte_count())
|
if (boundary >= text.byte_count())
|
||||||
return {};
|
return static_cast<i32>(text.byte_count());
|
||||||
|
|
||||||
U8_SET_CP_START(text.bytes().data(), 0, icu_boundary);
|
U8_SET_CP_START(text.bytes().data(), 0, icu_boundary);
|
||||||
return icu_boundary;
|
return icu_boundary;
|
||||||
},
|
},
|
||||||
[&](icu::UnicodeString const& text) -> Optional<i32> {
|
[&](icu::UnicodeString const& text) {
|
||||||
if (icu_boundary >= text.length())
|
if (icu_boundary >= text.length())
|
||||||
return {};
|
return text.length();
|
||||||
|
|
||||||
return text.getChar32Start(icu_boundary);
|
return text.getChar32Start(icu_boundary);
|
||||||
},
|
},
|
||||||
[](Empty) -> Optional<i32> { VERIFY_NOT_REACHED(); });
|
[](Empty) -> i32 { VERIFY_NOT_REACHED(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void for_each_boundary(SegmentationCallback callback)
|
void for_each_boundary(SegmentationCallback callback)
|
||||||
|
|
|
@ -136,11 +136,23 @@ TEST_CASE(out_of_bounds)
|
||||||
auto segmenter = Unicode::Segmenter::create(Unicode::SegmenterGranularity::Word);
|
auto segmenter = Unicode::Segmenter::create(Unicode::SegmenterGranularity::Word);
|
||||||
segmenter->set_segmented_text(text);
|
segmenter->set_segmented_text(text);
|
||||||
|
|
||||||
auto result = segmenter->previous_boundary(text.byte_count());
|
auto result = segmenter->previous_boundary(text.byte_count() + 1);
|
||||||
|
EXPECT(result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->next_boundary(text.byte_count() + 1);
|
||||||
EXPECT(!result.has_value());
|
EXPECT(!result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->previous_boundary(text.byte_count());
|
||||||
|
EXPECT(result.has_value());
|
||||||
|
|
||||||
result = segmenter->next_boundary(text.byte_count());
|
result = segmenter->next_boundary(text.byte_count());
|
||||||
EXPECT(!result.has_value());
|
EXPECT(!result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->next_boundary(0);
|
||||||
|
EXPECT(result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->previous_boundary(0);
|
||||||
|
EXPECT(!result.has_value());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto text = MUST(AK::utf8_to_utf16("foo"sv));
|
auto text = MUST(AK::utf8_to_utf16("foo"sv));
|
||||||
|
@ -148,10 +160,22 @@ TEST_CASE(out_of_bounds)
|
||||||
auto segmenter = Unicode::Segmenter::create(Unicode::SegmenterGranularity::Word);
|
auto segmenter = Unicode::Segmenter::create(Unicode::SegmenterGranularity::Word);
|
||||||
segmenter->set_segmented_text(Utf16View { text });
|
segmenter->set_segmented_text(Utf16View { text });
|
||||||
|
|
||||||
auto result = segmenter->previous_boundary(text.size());
|
auto result = segmenter->previous_boundary(text.size() + 1);
|
||||||
|
EXPECT(result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->next_boundary(text.size() + 1);
|
||||||
EXPECT(!result.has_value());
|
EXPECT(!result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->previous_boundary(text.size());
|
||||||
|
EXPECT(result.has_value());
|
||||||
|
|
||||||
result = segmenter->next_boundary(text.size());
|
result = segmenter->next_boundary(text.size());
|
||||||
EXPECT(!result.has_value());
|
EXPECT(!result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->next_boundary(0);
|
||||||
|
EXPECT(result.has_value());
|
||||||
|
|
||||||
|
result = segmenter->previous_boundary(0);
|
||||||
|
EXPECT(!result.has_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue