Commit 8354c6e9 authored by manukh's avatar manukh Committed by Chromium LUCI CQ

[omnibox] [rich-autocompletion] Preserve cursor for split selections.

Split AC was added in CL crrev.com/c/2424985 . It allows e.g. the input
'a c' to match the text 'a [b ]c[ d]'

When generating the text selections from word matches, it removes empty
matches and merges adjacent matches. E.g. the input 'a b' produces the
selections 'a b[ c]' instead of 'a| |b[ c]'. This is desirable usually,
but not desirable for the last selection which is used to determine the
cursor position; merging or filtering the last selection incorrectly
alters the cursor.

This CL makes sure to preserve the last selection and, therefore, the
correct cursor. E.g. the input 'a c' produces selections 'a [b ]c|'
instead of 'a [b ]c'.

Bug: 1062446
Change-Id: Icfb13321d86454a8a2fbc89289fefda53287b770
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2601046
Commit-Queue: manuk hovanesian <manukh@chromium.org>
Reviewed-by: default avatarOrin Jaworski <orinj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839170}
parent 6f4d6ac7
...@@ -1315,7 +1315,7 @@ bool AutocompleteMatch::TryRichAutocompletion( ...@@ -1315,7 +1315,7 @@ bool AutocompleteMatch::TryRichAutocompletion(
// |fill_into_edit| should already be set to |primary_text|. // |fill_into_edit| should already be set to |primary_text|.
split_autocompletion = SplitAutocompletion( split_autocompletion = SplitAutocompletion(
primary_text_lower, primary_text_lower,
InvertAndReverseRanges(primary_text_lower.length(), input_words)); TermMatchesToSelections(primary_text_lower.length(), input_words));
allowed_to_be_default_match = true; allowed_to_be_default_match = true;
RecordAdditionalInfo("autocompletion", "primary & split"); RecordAdditionalInfo("autocompletion", "primary & split");
return true; return true;
...@@ -1340,7 +1340,7 @@ bool AutocompleteMatch::TryRichAutocompletion( ...@@ -1340,7 +1340,7 @@ bool AutocompleteMatch::TryRichAutocompletion(
swapped_fill_into_edit = true; swapped_fill_into_edit = true;
split_autocompletion = SplitAutocompletion( split_autocompletion = SplitAutocompletion(
secondary_text_lower, secondary_text_lower,
InvertAndReverseRanges(secondary_text_lower.length(), input_words)); TermMatchesToSelections(secondary_text_lower.length(), input_words));
allowed_to_be_default_match = true; allowed_to_be_default_match = true;
RecordAdditionalInfo("autocompletion", "secondary & split"); RecordAdditionalInfo("autocompletion", "secondary & split");
return true; return true;
......
...@@ -58,22 +58,30 @@ std::vector<std::pair<size_t, size_t>> FindWordsSequentiallyAtWordbreak( ...@@ -58,22 +58,30 @@ std::vector<std::pair<size_t, size_t>> FindWordsSequentiallyAtWordbreak(
return occurrences; return occurrences;
} }
std::vector<gfx::Range> InvertAndReverseRanges( std::vector<gfx::Range> TermMatchesToSelections(
size_t length, size_t length,
std::vector<std::pair<size_t, size_t>> ranges) { std::vector<std::pair<size_t, size_t>> matches) {
std::vector<gfx::Range> inverted; std::vector<gfx::Range> selections;
size_t cursor = length; size_t cursor = length;
for (size_t i = ranges.size(); i-- != 0;) { for (size_t i = matches.size(); i-- != 0;) {
auto range = ranges[i]; auto match = matches[i];
// Skip empty ranges. // Include the 1st iterated match as is. The 1st match determines the user's
if (range.first == range.second) // cursor, and skipping or merging the match would incorrectly move the
// user's cursor.
if (i == matches.size() - 1) {
selections.emplace_back(cursor, match.second);
cursor = match.first;
continue; continue;
// Merge adjacent ranges. }
if (cursor != range.second) // Skip empty matches.
inverted.emplace_back(cursor, range.second); if (match.first == match.second)
cursor = range.first; continue;
// Merge adjacent matches.
if (cursor != match.second)
selections.emplace_back(cursor, match.second);
cursor = match.first;
} }
if (cursor != 0) if (cursor != 0)
inverted.emplace_back(cursor, 0); selections.emplace_back(cursor, 0);
return inverted; return selections;
} }
...@@ -30,13 +30,14 @@ std::vector<std::pair<size_t, size_t>> FindWordsSequentiallyAtWordbreak( ...@@ -30,13 +30,14 @@ std::vector<std::pair<size_t, size_t>> FindWordsSequentiallyAtWordbreak(
const base::string16& text, const base::string16& text,
const base::string16& search); const base::string16& search);
// Inverts and reverses |ranges| in a domain of [0, |length|). Ranges are // Inverts and reverses term |matches| in a domain of [0, |length|) to determine
// interpreted as {start, end}. E.g., if |length| is 10 and |ranges| are // the selected non-matches. Ranges are interpreted as {start, end}. E.g., if
// {{2, 3} {5, 9}}, th |InvertRanges| will return {{10, 9}, {5, 3}, {2, 0}}. // |length| is 10 and |matches| are {{2, 3} {5, 9}}, |InvertRanges| will return
// Assumes |ranges| is in forward order; i.e. |ranges[i+1]| occurs after // {{10, 9}, {5, 3}, {2, 0}}. Assumes |matches| is in forward order; i.e.
// |ranges[i]| and |ranges[i].second| after |ranges[i].first|. // |matches[i+1]| occurs after |matches[i]| and |matches[i].second| after
std::vector<gfx::Range> InvertAndReverseRanges( // |matches[i].first|.
std::vector<gfx::Range> TermMatchesToSelections(
size_t length, size_t length,
std::vector<std::pair<size_t, size_t>> ranges); std::vector<std::pair<size_t, size_t>> matches);
#endif // COMPONENTS_OMNIBOX_BROWSER_INLINE_AUTOCOMPLETION_UTIL_H_ #endif // COMPONENTS_OMNIBOX_BROWSER_INLINE_AUTOCOMPLETION_UTIL_H_
...@@ -75,29 +75,33 @@ TEST(InlineAutocompletionUtilTest, FindWordsSequentiallyAtWordbreak) { ...@@ -75,29 +75,33 @@ TEST(InlineAutocompletionUtilTest, FindWordsSequentiallyAtWordbreak) {
testing::ElementsAre(pair{0, 1}, pair{3, 5}, pair{5, 6})); testing::ElementsAre(pair{0, 1}, pair{3, 5}, pair{5, 6}));
} }
TEST(InlineAutocompletionUtilTest, InvertAndReverseRanges) { TEST(InlineAutocompletionUtilTest, TermMatchesToSelections) {
// Empty |ranges| in empty |length|. // Empty |ranges| in empty |length|.
EXPECT_THAT(InvertAndReverseRanges(0, {}), testing::ElementsAre()); EXPECT_THAT(TermMatchesToSelections(0, {}), testing::ElementsAre());
// Empty |ranges| in non-empty |length|. 12345 -> [12345] // Empty |ranges| in non-empty |length|. 12345 -> [12345]
EXPECT_THAT(InvertAndReverseRanges(5, {}), EXPECT_THAT(TermMatchesToSelections(5, {}),
testing::ElementsAre(gfx::Range{5, 0}));
// Single empty range in |ranges|. 12|345 -> [12345]
EXPECT_THAT(InvertAndReverseRanges(5, {{2, 2}}),
testing::ElementsAre(gfx::Range{5, 0})); testing::ElementsAre(gfx::Range{5, 0}));
// Single empty range in |ranges|. 12|345 -> [12][345]
EXPECT_THAT(TermMatchesToSelections(5, {{2, 2}}),
testing::ElementsAre(gfx::Range{5, 2}, gfx::Range{2, 0}));
// Multiple empty ranges in |ranges|. 1|2|3|4|5 -> [1234][5]
EXPECT_THAT(TermMatchesToSelections(5, {{1, 1}, {2, 2}, {3, 3}, {4, 4}}),
testing::ElementsAre(gfx::Range{5, 4}, gfx::Range{4, 0}));
// Single range in |ranges|. 12[3]45 -> [12]3[45] // Single range in |ranges|. 12[3]45 -> [12]3[45]
EXPECT_THAT(InvertAndReverseRanges(5, {{2, 3}}), EXPECT_THAT(TermMatchesToSelections(5, {{2, 3}}),
testing::ElementsAre(gfx::Range{5, 3}, gfx::Range{2, 0})); testing::ElementsAre(gfx::Range{5, 3}, gfx::Range{2, 0}));
// Single range in |ranges| spanning all of |length|. [12345] -> 12345 // Single range in |ranges| spanning all of |length|. [12345] -> 12345|
EXPECT_THAT(InvertAndReverseRanges(5, {{0, 5}}), testing::ElementsAre()); EXPECT_THAT(TermMatchesToSelections(5, {{0, 5}}),
testing::ElementsAre(gfx::Range{5, 5}));
// Multiple ranges in |ranges|, including adjacent and empty ranges. // Multiple ranges in |ranges|, including adjacent and empty ranges.
// 1[23][45]6[78]9 -> [1]2345[6]78[9] // 1[23][45]6[7][8]9 -> [1]2345[6]78[9]
EXPECT_THAT(InvertAndReverseRanges(9, {{1, 3}, {3, 5}, {6, 8}}), EXPECT_THAT(TermMatchesToSelections(9, {{1, 3}, {3, 5}, {6, 7}, {7, 8}}),
testing::ElementsAre(gfx::Range{9, 8}, gfx::Range{6, 5}, testing::ElementsAre(gfx::Range{9, 8}, gfx::Range{6, 5},
gfx::Range{1, 0})); gfx::Range{1, 0}));
// |ranges| ending at |length| and starting 0. // |ranges| ending at |length| and starting 0.
// [123][45]6[789] -> 12345[6]789 // [123][45]6[789] -> 12345[6]789|
EXPECT_THAT(InvertAndReverseRanges(9, {{0, 3}, {3, 5}, {6, 9}}), EXPECT_THAT(TermMatchesToSelections(9, {{0, 3}, {3, 5}, {6, 9}}),
testing::ElementsAre(gfx::Range{6, 5})); testing::ElementsAre(gfx::Range{9, 9}, gfx::Range{6, 5}));
} }
} // namespace } // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment