diff options
author | Eduard Zalyaev <e.zalyaev@innopolis.ru> | 2022-12-05 10:43:27 +0300 |
---|---|---|
committer | Eduard Zalyaev <e.zalyaev@innopolis.ru> | 2022-12-05 10:43:27 +0300 |
commit | c3eec67f06a1dd1dcc8169b8f5cdc245fd434630 (patch) | |
tree | 94d5f634c56d6f2821d68c6b057d24edc70f4480 | |
parent | e8f9cd8ac5cf3e511e02d78a5497d204ca7e8308 (diff) |
Script editor: Rewrite multiline move logic. Preserving all carets and their selections intact on alt+up/down shortcut
Fixes #68731.
-rw-r--r-- | editor/code_editor.cpp | 263 |
1 files changed, 137 insertions, 126 deletions
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 1e734bb97f..ee67f1765c 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -33,6 +33,7 @@ #include "core/input/input.h" #include "core/os/keyboard.h" #include "core/string/string_builder.h" +#include "core/templates/pair.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/script_editor_plugin.h" @@ -1290,90 +1291,98 @@ void CodeTextEditor::convert_case(CaseStyle p_case) { void CodeTextEditor::move_lines_up() { text_editor->begin_complex_operation(); - Vector<int> carets_to_remove; - Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order(); + + // Lists of carets representing each group + Vector<Vector<int>> caret_groups; + Vector<Pair<int, int>> group_borders; + + // Search for groups of carets and their selections residing on the same lines for (int i = 0; i < caret_edit_order.size(); i++) { int c = caret_edit_order[i]; - int cl = text_editor->get_caret_line(c); - bool swaped_caret = false; - for (int j = i + 1; j < caret_edit_order.size(); j++) { - if (text_editor->has_selection(caret_edit_order[j])) { - if (text_editor->get_selection_from_line() == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - continue; - } + Vector<int> new_group{ c }; + Pair<int, int> group_border; + if (text_editor->has_selection(c)) { + group_border.first = text_editor->get_selection_from_line(c); + group_border.second = text_editor->get_selection_to_line(c); + } else { + group_border.first = text_editor->get_caret_line(c); + group_border.second = text_editor->get_caret_line(c); + } - if (text_editor->get_selection_to_line() == cl) { - if (text_editor->has_selection(c)) { - if (text_editor->get_selection_to_line(c) != cl) { - text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c); - break; - } - } + for (int j = i; j < caret_edit_order.size() - 1; j++) { + int c_current = caret_edit_order[j]; + int c_next = caret_edit_order[j + 1]; - carets_to_remove.push_back(c); - i = j - 1; - swaped_caret = true; - break; - } + int next_start_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_from_line(c_next) : text_editor->get_caret_line(c_next); + int next_end_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_to_line(c_next) : text_editor->get_caret_line(c_next); + + int current_start_pos = text_editor->has_selection(c_current) ? text_editor->get_selection_from_line(c_current) : text_editor->get_caret_line(c_current); + + i = j; + if (next_end_pos != current_start_pos && next_end_pos + 1 != current_start_pos) { break; } - - if (text_editor->get_caret_line(caret_edit_order[j]) == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - i = j; - continue; + group_border.first = next_start_pos; + new_group.push_back(c_next); + // If the last caret is added to the current group there is no need to process it again + if (j + 1 == caret_edit_order.size() - 1) { + i++; } - break; } + group_borders.push_back(group_border); + caret_groups.push_back(new_group); + } - if (swaped_caret) { + for (int i = group_borders.size() - 1; i >= 0; i--) { + if (group_borders[i].first - 1 < 0) { continue; } - if (text_editor->has_selection(c)) { + // If the group starts overlapping with the upper group don't move it + if (i < group_borders.size() - 1 && group_borders[i].first - 1 <= group_borders[i + 1].second) { + continue; + } + + // We have to remember caret positions and selections prior to line swapping + Vector<Vector<int>> caret_group_parameters; + + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + int cursor_line = text_editor->get_caret_line(c); + int cursor_column = text_editor->get_caret_column(c); + + if (!text_editor->has_selection(c)) { + caret_group_parameters.push_back(Vector<int>{ -1, -1, -1, -1, cursor_line, cursor_column }); + continue; + } int from_line = text_editor->get_selection_from_line(c); int from_col = text_editor->get_selection_from_column(c); int to_line = text_editor->get_selection_to_line(c); int to_column = text_editor->get_selection_to_column(c); - int cursor_line = text_editor->get_caret_line(c); - - for (int j = from_line; j <= to_line; j++) { - int line_id = j; - int next_id = j - 1; + caret_group_parameters.push_back(Vector<int>{ from_line, from_col, to_line, to_column, cursor_line, cursor_column }); + } - if (line_id == 0 || next_id < 0) { - return; - } + for (int line_id = group_borders[i].first; line_id <= group_borders[i].second; line_id++) { + text_editor->unfold_line(line_id); + text_editor->unfold_line(line_id - 1); - text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); + text_editor->swap_lines(line_id - 1, line_id); + } - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); - } - int from_line_up = from_line > 0 ? from_line - 1 : from_line; - int to_line_up = to_line > 0 ? to_line - 1 : to_line; - int cursor_line_up = cursor_line > 0 ? cursor_line - 1 : cursor_line; - text_editor->select(from_line_up, from_col, to_line_up, to_column, c); - text_editor->set_caret_line(cursor_line_up, c == 0, true, 0, c); - } else { - int line_id = text_editor->get_caret_line(c); - int next_id = line_id - 1; + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + Vector<int> caret_parameters = caret_group_parameters[j]; + text_editor->set_caret_line(caret_parameters[4] - 1, c == 0, true, 0, c); + text_editor->set_caret_column(caret_parameters[5], c == 0, c); - if (line_id == 0 || next_id < 0) { - return; + if (caret_parameters[0] >= 0) { + text_editor->select(caret_parameters[0] - 1, caret_parameters[1], caret_parameters[2] - 1, caret_parameters[3], c); } - - text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); - - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); } } + text_editor->end_complex_operation(); text_editor->merge_overlapping_carets(); text_editor->queue_redraw(); @@ -1382,95 +1391,97 @@ void CodeTextEditor::move_lines_up() { void CodeTextEditor::move_lines_down() { text_editor->begin_complex_operation(); - Vector<int> carets_to_remove; - Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order(); + + // Lists of carets representing each group + Vector<Vector<int>> caret_groups; + Vector<Pair<int, int>> group_borders; + + // Search for groups of carets and their selections residing on the same lines for (int i = 0; i < caret_edit_order.size(); i++) { int c = caret_edit_order[i]; - int cl = text_editor->get_caret_line(c); - bool swaped_caret = false; - for (int j = i + 1; j < caret_edit_order.size(); j++) { - if (text_editor->has_selection(caret_edit_order[j])) { - if (text_editor->get_selection_from_line() == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - continue; - } + Vector<int> new_group{ c }; + Pair<int, int> group_border; + if (text_editor->has_selection(c)) { + group_border.first = text_editor->get_selection_from_line(c); + group_border.second = text_editor->get_selection_to_line(c); + } else { + group_border.first = text_editor->get_caret_line(c); + group_border.second = text_editor->get_caret_line(c); + } - if (text_editor->get_selection_to_line() == cl) { - if (text_editor->has_selection(c)) { - if (text_editor->get_selection_to_line(c) != cl) { - text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c); - break; - } - } + for (int j = i; j < caret_edit_order.size() - 1; j++) { + int c_current = caret_edit_order[j]; + int c_next = caret_edit_order[j + 1]; - carets_to_remove.push_back(c); - i = j - 1; - swaped_caret = true; - break; + int next_start_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_from_line(c_next) : text_editor->get_caret_line(c_next); + int next_end_pos = text_editor->has_selection(c_next) ? text_editor->get_selection_to_line(c_next) : text_editor->get_caret_line(c_next); + + int current_start_pos = text_editor->has_selection(c_current) ? text_editor->get_selection_from_line(c_current) : text_editor->get_caret_line(c_current); + + i = j; + if (next_end_pos == current_start_pos || next_end_pos + 1 == current_start_pos) { + group_border.first = next_start_pos; + new_group.push_back(c_next); + // If the last caret is added to the current group there is no need to process it again + if (j + 1 == caret_edit_order.size() - 1) { + i++; } + } else { break; } - - if (text_editor->get_caret_line(caret_edit_order[j]) == cl) { - carets_to_remove.push_back(caret_edit_order[j]); - i = j; - continue; - } - break; } + group_borders.push_back(group_border); + caret_groups.push_back(new_group); + } - if (swaped_caret) { + for (int i = 0; i < group_borders.size(); i++) { + if (group_borders[i].second + 1 > text_editor->get_line_count() - 1) { continue; } - if (text_editor->has_selection(c)) { - int from_line = text_editor->get_selection_from_line(c); - int from_col = text_editor->get_selection_from_column(c); - int to_line = text_editor->get_selection_to_line(c); - int to_column = text_editor->get_selection_to_column(c); - int cursor_line = text_editor->get_caret_line(c); - - for (int l = to_line; l >= from_line; l--) { - int line_id = l; - int next_id = l + 1; - - if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) { - continue; - } - - text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); + // If the group starts overlapping with the upper group don't move it + if (i > 0 && group_borders[i].second + 1 >= group_borders[i - 1].first) { + continue; + } - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); - } - int from_line_down = from_line < text_editor->get_line_count() ? from_line + 1 : from_line; - int to_line_down = to_line < text_editor->get_line_count() ? to_line + 1 : to_line; - int cursor_line_down = cursor_line < text_editor->get_line_count() ? cursor_line + 1 : cursor_line; - text_editor->select(from_line_down, from_col, to_line_down, to_column, c); - text_editor->set_caret_line(cursor_line_down, c == 0, true, 0, c); - } else { - int line_id = text_editor->get_caret_line(c); - int next_id = line_id + 1; + // We have to remember caret positions and selections prior to line swapping + Vector<Vector<int>> caret_group_parameters; - if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) { - continue; + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + int cursor_line = text_editor->get_caret_line(c); + int cursor_column = text_editor->get_caret_column(c); + + if (text_editor->has_selection(c)) { + int from_line = text_editor->get_selection_from_line(c); + int from_col = text_editor->get_selection_from_column(c); + int to_line = text_editor->get_selection_to_line(c); + int to_column = text_editor->get_selection_to_column(c); + caret_group_parameters.push_back(Vector<int>{ from_line, from_col, to_line, to_column, cursor_line, cursor_column }); + } else { + caret_group_parameters.push_back(Vector<int>{ -1, -1, -1, -1, cursor_line, cursor_column }); } + } + for (int line_id = group_borders[i].second; line_id >= group_borders[i].first; line_id--) { text_editor->unfold_line(line_id); - text_editor->unfold_line(next_id); + text_editor->unfold_line(line_id + 1); - text_editor->swap_lines(line_id, next_id); - text_editor->set_caret_line(next_id, c == 0, true, 0, c); + text_editor->swap_lines(line_id + 1, line_id); } - } - // Sort and remove backwards to preserve indexes. - carets_to_remove.sort(); - for (int i = carets_to_remove.size() - 1; i >= 0; i--) { - text_editor->remove_caret(carets_to_remove[i]); + for (int j = 0; j < caret_groups[i].size(); j++) { + int c = caret_groups[i][j]; + Vector<int> caret_parameters = caret_group_parameters[j]; + text_editor->set_caret_line(caret_parameters[4] + 1, c == 0, true, 0, c); + text_editor->set_caret_column(caret_parameters[5], c == 0, c); + + if (caret_parameters[0] >= 0) { + text_editor->select(caret_parameters[0] + 1, caret_parameters[1], caret_parameters[2] + 1, caret_parameters[3], c); + } + } } text_editor->merge_overlapping_carets(); |