Browse Source

Script editor: Rewrite multiline move logic. Preserving all carets and their selections intact on alt+up/down shortcut

Fixes  #68731.
Eduard Zalyaev 2 years ago
parent
commit
c3eec67f06
1 changed files with 137 additions and 126 deletions
  1. 137 126
      editor/code_editor.cpp

+ 137 - 126
editor/code_editor.cpp

@@ -33,6 +33,7 @@
 #include "core/input/input.h"
 #include "core/input/input.h"
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "core/string/string_builder.h"
 #include "core/string/string_builder.h"
+#include "core/templates/pair.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor/plugins/script_editor_plugin.h"
@@ -1290,90 +1291,98 @@ void CodeTextEditor::convert_case(CaseStyle p_case) {
 void CodeTextEditor::move_lines_up() {
 void CodeTextEditor::move_lines_up() {
 	text_editor->begin_complex_operation();
 	text_editor->begin_complex_operation();
 
 
-	Vector<int> carets_to_remove;
-
 	Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
 	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++) {
 	for (int i = 0; i < caret_edit_order.size(); i++) {
 		int c = caret_edit_order[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;
 				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;
 			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_line = text_editor->get_selection_from_line(c);
 			int from_col = text_editor->get_selection_from_column(c);
 			int from_col = text_editor->get_selection_from_column(c);
 			int to_line = text_editor->get_selection_to_line(c);
 			int to_line = text_editor->get_selection_to_line(c);
 			int to_column = text_editor->get_selection_to_column(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->end_complex_operation();
 	text_editor->merge_overlapping_carets();
 	text_editor->merge_overlapping_carets();
 	text_editor->queue_redraw();
 	text_editor->queue_redraw();
@@ -1382,95 +1391,97 @@ void CodeTextEditor::move_lines_up() {
 void CodeTextEditor::move_lines_down() {
 void CodeTextEditor::move_lines_down() {
 	text_editor->begin_complex_operation();
 	text_editor->begin_complex_operation();
 
 
-	Vector<int> carets_to_remove;
-
 	Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
 	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++) {
 	for (int i = 0; i < caret_edit_order.size(); i++) {
 		int c = caret_edit_order[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;
 				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;
 			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(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();
 	text_editor->merge_overlapping_carets();