Merge pull request #93607 from kitbdev/fix-text-edit-move-lines

CodeEdit: Fix move lines up/down viewport and selection issues
This commit is contained in:
Rémi Verschelde 2024-06-27 17:08:23 +02:00
commit 3f4bcfeef1
No known key found for this signature in database
GPG key ID: C3336907360768E1
2 changed files with 153 additions and 23 deletions

View file

@ -2331,18 +2331,19 @@ void CodeEdit::move_lines_up() {
unfold_line(line);
swap_lines(line - 1, line);
}
}
// Fix selection if it ended at column 0, since it wasn't moved.
for (int i = 0; i < get_caret_count(); i++) {
if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) != 0) {
if (is_caret_after_selection_origin(i)) {
set_caret_line(get_caret_line(i) - 1, false, true, -1, i);
} else {
set_selection_origin_line(get_selection_origin_line(i) - 1, true, -1, i);
// Fix selection if the last one ends at column 0, since it wasn't moved.
for (int i = 0; i < get_caret_count(); i++) {
if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) == line_range.y + 1) {
if (is_caret_after_selection_origin(i)) {
set_caret_line(get_caret_line(i) - 1, false, true, -1, i);
} else {
set_selection_origin_line(get_selection_origin_line(i) - 1, true, -1, i);
}
break;
}
}
}
adjust_viewport_to_caret();
end_multicaret_edit();
end_complex_operation();
@ -2352,30 +2353,41 @@ void CodeEdit::move_lines_down() {
begin_complex_operation();
begin_multicaret_edit();
Vector<Point2i> line_ranges = get_line_ranges_from_carets();
// Fix selection if it ended at column 0, since it won't be moved.
for (int i = 0; i < get_caret_count(); i++) {
if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) != get_line_count() - 1) {
if (is_caret_after_selection_origin(i)) {
set_caret_line(get_caret_line(i) + 1, false, true, -1, i);
} else {
set_selection_origin_line(get_selection_origin_line(i) + 1, true, -1, i);
}
}
}
// Move lines down by swapping each line with the one below it.
Vector<Point2i> line_ranges = get_line_ranges_from_carets();
// Reverse in case line ranges are adjacent, if the first ends at column 0.
line_ranges.reverse();
for (Point2i line_range : line_ranges) {
if (line_range.y == get_line_count() - 1) {
continue;
}
// Fix selection if the last one ends at column 0, since it won't be moved.
bool selection_to_line_at_end = false;
for (int i = 0; i < get_caret_count(); i++) {
if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) == line_range.y + 1) {
selection_to_line_at_end = get_selection_to_line(i) == get_line_count() - 1;
if (selection_to_line_at_end) {
break;
}
if (is_caret_after_selection_origin(i)) {
set_caret_line(get_caret_line(i) + 1, false, true, -1, i);
} else {
set_selection_origin_line(get_selection_origin_line(i) + 1, true, -1, i);
}
break;
}
}
if (selection_to_line_at_end) {
continue;
}
unfold_line(line_range.y + 1);
for (int line = line_range.y; line >= line_range.x; line--) {
unfold_line(line);
swap_lines(line + 1, line);
}
}
adjust_viewport_to_caret();
end_multicaret_edit();
end_complex_operation();

View file

@ -4889,6 +4889,17 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK(code_edit->get_caret_line() == 0);
CHECK(code_edit->get_caret_column() == 1);
// Does nothing at the first line when selection ends at column 0.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->select(0, 0, 1, 0);
code_edit->move_lines_up();
CHECK(code_edit->get_text() == "test\nlines\nto\n\nmove\naround");
CHECK(code_edit->has_selection());
CHECK(code_edit->get_selection_origin_line() == 0);
CHECK(code_edit->get_selection_origin_column() == 0);
CHECK(code_edit->get_caret_line() == 1);
CHECK(code_edit->get_caret_column() == 0);
// Works on empty line.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->set_caret_line(3);
@ -4950,9 +4961,9 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK_FALSE(code_edit->has_selection(2));
CHECK(code_edit->get_caret_line(2) == 3);
CHECK(code_edit->get_caret_column(2) == 4);
code_edit->remove_secondary_carets();
// Move multiple separate lines with multiple selections.
code_edit->remove_secondary_carets();
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->select(2, 2, 1, 4);
code_edit->add_caret(5, 0);
@ -4970,6 +4981,44 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK(code_edit->get_selection_origin_column(1) == 0);
CHECK(code_edit->get_caret_line(1) == 4);
CHECK(code_edit->get_caret_column(1) == 1);
code_edit->remove_secondary_carets();
// Move lines with adjacent selections that end at column 0.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->select(1, 2, 2, 0);
code_edit->add_caret(2, 2);
code_edit->select(2, 2, 3, 0, 1);
code_edit->move_lines_up();
CHECK(code_edit->get_text() == "lines\nto\ntest\n\nmove\naround");
CHECK(code_edit->get_caret_count() == 2);
CHECK(code_edit->has_selection(0));
CHECK(code_edit->get_selection_origin_line(0) == 0);
CHECK(code_edit->get_selection_origin_column(0) == 2);
CHECK(code_edit->get_caret_line(0) == 1);
CHECK(code_edit->get_caret_column(0) == 0);
CHECK(code_edit->has_selection(1));
CHECK(code_edit->get_selection_origin_line(1) == 1);
CHECK(code_edit->get_selection_origin_column(1) == 2);
CHECK(code_edit->get_caret_line(1) == 2);
CHECK(code_edit->get_caret_column(1) == 0);
code_edit->remove_secondary_carets();
code_edit->deselect();
code_edit->set_line_folding_enabled(true);
// Move line up into a folded region unfolds it.
code_edit->set_text("test\n\tline1 test\n\t\tline 2\ntest2");
code_edit->set_caret_line(3);
code_edit->set_caret_column(0);
code_edit->fold_line(0);
CHECK(code_edit->is_line_folded(0));
code_edit->move_lines_up();
CHECK(code_edit->get_caret_count() == 1);
CHECK_FALSE(code_edit->has_selection(0));
CHECK(code_edit->get_caret_line() == 2);
CHECK(code_edit->get_caret_column() == 0);
CHECK(code_edit->get_text() == "test\n\tline1 test\ntest2\n\t\tline 2");
CHECK_FALSE(code_edit->is_line_folded(0));
}
SUBCASE("[SceneTree][CodeEdit] move lines down") {
@ -5004,6 +5053,17 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK(code_edit->get_caret_line() == 5);
CHECK(code_edit->get_caret_column() == 1);
// Does nothing at the last line when selection ends at column 0.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->select(4, 0, 5, 0);
code_edit->move_lines_down();
CHECK(code_edit->get_text() == "test\nlines\nto\n\nmove\naround");
CHECK(code_edit->has_selection());
CHECK(code_edit->get_selection_origin_line() == 4);
CHECK(code_edit->get_selection_origin_column() == 0);
CHECK(code_edit->get_caret_line() == 5);
CHECK(code_edit->get_caret_column() == 0);
// Works on empty line.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->set_caret_line(3);
@ -5085,6 +5145,64 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK(code_edit->get_selection_origin_column(1) == 0);
CHECK(code_edit->get_caret_line(1) == 5);
CHECK(code_edit->get_caret_column(1) == 2);
// Move lines with adjacent selections that end at column 0.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->select(1, 2, 2, 0);
code_edit->add_caret(2, 2);
code_edit->select(2, 2, 3, 0, 1);
code_edit->move_lines_down();
CHECK(code_edit->get_text() == "test\n\nlines\nto\nmove\naround");
CHECK(code_edit->get_caret_count() == 2);
CHECK(code_edit->has_selection(0));
CHECK(code_edit->get_selection_origin_line(0) == 2);
CHECK(code_edit->get_selection_origin_column(0) == 2);
CHECK(code_edit->get_caret_line(0) == 3);
CHECK(code_edit->get_caret_column(0) == 0);
CHECK(code_edit->has_selection(1));
CHECK(code_edit->get_selection_origin_line(1) == 3);
CHECK(code_edit->get_selection_origin_column(1) == 2);
CHECK(code_edit->get_caret_line(1) == 4);
CHECK(code_edit->get_caret_column(1) == 0);
code_edit->remove_secondary_carets();
// Move lines with disconnected adjacent selections that end at column 0.
code_edit->set_text("test\nlines\nto\n\nmove\naround");
code_edit->select(0, 2, 1, 0);
code_edit->add_caret(2, 2);
code_edit->select(2, 0, 3, 0, 1);
code_edit->move_lines_down();
CHECK(code_edit->get_text() == "lines\ntest\n\nto\nmove\naround");
CHECK(code_edit->get_caret_count() == 2);
CHECK(code_edit->has_selection(0));
CHECK(code_edit->get_selection_origin_line(0) == 1);
CHECK(code_edit->get_selection_origin_column(0) == 2);
CHECK(code_edit->get_caret_line(0) == 2);
CHECK(code_edit->get_caret_column(0) == 0);
CHECK(code_edit->has_selection(1));
CHECK(code_edit->get_selection_origin_line(1) == 3);
CHECK(code_edit->get_selection_origin_column(1) == 0);
CHECK(code_edit->get_caret_line(1) == 4);
CHECK(code_edit->get_caret_column(1) == 0);
code_edit->remove_secondary_carets();
code_edit->deselect();
code_edit->set_line_folding_enabled(true);
// Move line down into a folded region unfolds it.
code_edit->set_text("test\ntest2\n\tline1 test\n\t\tline 2\ntest2");
code_edit->set_caret_line(0);
code_edit->set_caret_column(0);
code_edit->fold_line(1);
CHECK(code_edit->is_line_folded(1));
code_edit->move_lines_down();
CHECK(code_edit->get_caret_count() == 1);
CHECK_FALSE(code_edit->has_selection(0));
CHECK(code_edit->get_caret_line() == 1);
CHECK(code_edit->get_caret_column() == 0);
CHECK(code_edit->get_text() == "test2\ntest\n\tline1 test\n\t\tline 2\ntest2");
CHECK_FALSE(code_edit->is_line_folded(0));
CHECK_FALSE(code_edit->is_line_folded(1));
}
SUBCASE("[SceneTree][CodeEdit] delete lines") {