Fix words not being selected by endpoints
Previously, words were unable to be selected by placing the cursor at the left-most character or the character directly after the word. This issue is addressed by adjusting the conditions to check if the caret is positioned in these places.
While fixing this issue, I found that my edits to the conditions affected the behaviour of drag-selecting multiple words. Specifically, it made it impossible to select the whitespace between words in word selection mode, which was a feature before. This is fixed by modifying the conditions based on if multiple words are being selected.
Another problem related to how selecting the word by the leftmost character moved the caret to the left side of the word, while selecting it by any other character moved it to the right side. This is fixed by preventing the word selection from expanding to the left side.
In addition, multiple unit tests were added/changed. One test relied on the previous behaviour and was therefore changed. Additional tests were added to test the selection from left and right endpoints, as well as the deselection by moving the caret back to the original selection position.
Fixes #89551
This works as expected, but I'm not sure if it would work after https://github.com/godotengine/godot/pull/86978 fixes the ability to word select empty lines or shift double click to word select from caret, since that may change the logic.
#86978 does change the logic a little bit.
I would say the p_initial parameter should be used instead of the has_selection function for clarity purposes. Additionally, the condition that I added to fix the caret moving to the left after selecting can be moved into the else block.
The only thing I would change from the logic added in #86978 is to change the condition for is_new_selection_dir_right to not be true when the caret is at the leftmost character. Using column > instead of column >= resolves the issue where the editor selects the range from the beginning to the beginning instead of the word.
void TextEdit::_update_selection_mode_word(bool p_initial) {
...
for (int i = 0; i < words.size(); i = i + 2) {
// (1) use p_initial instead of has_selection()
if ((p_initial && words[i] <= caret_pos && words[i + 1] >= caret_pos) || (!p_initial && words[i] < caret_pos && words[i + 1] > caret_pos) || (i == words.size() - 2 && caret_pos == words[i + 1])) {
beg = words[i];
end = words[i + 1];
break;
}
}
if (p_initial && !has_selection(caret_index)) {
// Set the selection origin if there is no existing selection.
...
moved_word_selection = false;
} else {
// Expand the word selection to the mouse.
...
// (3) 'column >' instead of 'column >='
bool is_new_selection_dir_right = line > origin_line || (line == origin_line && column > carets[caret_index].selection.word_begin_column);
...
// (2) move the "moved word selection" condition here instead
if (column != carets[caret_index].selection.word_begin_column || line != origin_line || moved_word_selection) {
select(origin_line, origin_col, line, caret_col, caret_index);
moved_word_selection = true;
}
}
...
}
These changes seem to work well when I add them to your fork.
Rebased to resolve conflicts with #86978.
Also altered one test that used the previous behavior when double-clicking the endpoint of a word, and added two tests to capture the behavior of selecting a word by its endpoints. I tried to avoid altering the tests as much as possible, but please let me know if there's a better way.
Thanks!