Explorar o código

Add multi caret support to CodeEdit

Paulb23 %!s(int64=3) %!d(string=hai) anos
pai
achega
e9180241e8
Modificáronse 1 ficheiros con 396 adicións e 349 borrados
  1. 396 349
      scene/gui/code_edit.cpp

+ 396 - 349
scene/gui/code_edit.cpp

@@ -610,68 +610,70 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
 
 // Overridable actions
 void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode, int p_caret) {
-	bool had_selection = has_selection();
-	String selection_text = (had_selection ? get_selected_text() : "");
+	start_action(EditAction::ACTION_TYPING);
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &i : caret_edit_order) {
+		if (p_caret != -1 && p_caret != i) {
+			continue;
+		}
 
-	if (had_selection) {
-		begin_complex_operation();
-		delete_selection();
-	}
+		bool had_selection = has_selection(i);
+		String selection_text = (had_selection ? get_selected_text(i) : "");
 
-	// Remove the old character if in overtype mode and no selection.
-	if (is_overtype_mode_enabled() && !had_selection) {
-		begin_complex_operation();
+		if (had_selection) {
+			delete_selection(i);
+		}
 
-		/* Make sure we don't try and remove empty space. */
-		if (get_caret_column() < get_line(get_caret_line()).length()) {
-			remove_text(get_caret_line(), get_caret_column(), get_caret_line(), get_caret_column() + 1);
+		// Remove the old character if in overtype mode and no selection.
+		if (is_overtype_mode_enabled() && !had_selection) {
+			// Make sure we don't try and remove empty space.
+			if (get_caret_column(i) < get_line(get_caret_line(i)).length()) {
+				remove_text(get_caret_line(i), get_caret_column(i), get_caret_line(i), get_caret_column(i) + 1);
+			}
 		}
-	}
 
-	const char32_t chr[2] = { (char32_t)p_unicode, 0 };
+		const char32_t chr[2] = { (char32_t)p_unicode, 0 };
 
-	if (auto_brace_completion_enabled) {
-		int cl = get_caret_line();
-		int cc = get_caret_column();
+		if (auto_brace_completion_enabled) {
+			int cl = get_caret_line(i);
+			int cc = get_caret_column(i);
 
-		if (had_selection) {
-			insert_text_at_caret(chr);
+			if (had_selection) {
+				insert_text_at_caret(chr, i);
 
-			String close_key = get_auto_brace_completion_close_key(chr);
-			if (!close_key.is_empty()) {
-				insert_text_at_caret(selection_text + close_key);
-				set_caret_column(get_caret_column() - 1);
-			}
-		} else {
-			int caret_move_offset = 1;
-
-			int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
-
-			if (has_string_delimiter(chr) && cc > 0 && !is_symbol(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
-				insert_text_at_caret(chr);
-			} else if (cc < get_line(cl).length() && !is_symbol(get_line(cl)[cc])) {
-				insert_text_at_caret(chr);
-			} else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
-				caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
-			} else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
-				insert_text_at_caret(chr);
+				String close_key = get_auto_brace_completion_close_key(chr);
+				if (!close_key.is_empty()) {
+					insert_text_at_caret(selection_text + close_key, i);
+					set_caret_column(get_caret_column(i) - 1, i == 0, i);
+				}
 			} else {
-				insert_text_at_caret(chr);
+				int caret_move_offset = 1;
+
+				int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
+
+				if (has_string_delimiter(chr) && cc > 0 && !is_symbol(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+					insert_text_at_caret(chr, i);
+				} else if (cc < get_line(cl).length() && !is_symbol(get_line(cl)[cc])) {
+					insert_text_at_caret(chr, i);
+				} else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
+					caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
+				} else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+					insert_text_at_caret(chr, i);
+				} else {
+					insert_text_at_caret(chr, i);
 
-				int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
-				if (pre_brace_pair != -1) {
-					insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+					int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
+					if (pre_brace_pair != -1) {
+						insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key, i);
+					}
 				}
+				set_caret_column(cc + caret_move_offset, i == 0, i);
 			}
-			set_caret_column(cc + caret_move_offset);
+		} else {
+			insert_text_at_caret(chr, i);
 		}
-	} else {
-		insert_text_at_caret(chr);
-	}
-
-	if ((is_overtype_mode_enabled() && !had_selection) || (had_selection)) {
-		end_complex_operation();
 	}
+	end_action();
 }
 
 void CodeEdit::_backspace_internal(int p_caret) {
@@ -684,51 +686,65 @@ void CodeEdit::_backspace_internal(int p_caret) {
 		return;
 	}
 
-	int cc = get_caret_column();
-	int cl = get_caret_line();
+	begin_complex_operation();
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &i : caret_edit_order) {
+		if (p_caret != -1 && p_caret != i) {
+			continue;
+		}
 
-	if (cc == 0 && cl == 0) {
-		return;
-	}
+		int cc = get_caret_column(i);
+		int cl = get_caret_line(i);
 
-	if (cl > 0 && _is_line_hidden(cl - 1)) {
-		unfold_line(get_caret_line() - 1);
-	}
+		if (cc == 0 && cl == 0) {
+			continue;
+		}
 
-	int prev_line = cc ? cl : cl - 1;
-	int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length());
+		if (cl > 0 && _is_line_hidden(cl - 1)) {
+			unfold_line(get_caret_line(i) - 1);
+		}
 
-	merge_gutters(prev_line, cl);
+		int prev_line = cc ? cl : cl - 1;
+		int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length());
 
-	if (auto_brace_completion_enabled && cc > 0) {
-		int idx = _get_auto_brace_pair_open_at_pos(cl, cc);
-		if (idx != -1) {
-			prev_column = cc - auto_brace_completion_pairs[idx].open_key.length();
+		merge_gutters(prev_line, cl);
 
-			if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) {
-				remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
-			} else {
-				remove_text(prev_line, prev_column, cl, cc);
+		if (auto_brace_completion_enabled && cc > 0) {
+			int idx = _get_auto_brace_pair_open_at_pos(cl, cc);
+			if (idx != -1) {
+				prev_column = cc - auto_brace_completion_pairs[idx].open_key.length();
+
+				if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) {
+					remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
+				} else {
+					remove_text(prev_line, prev_column, cl, cc);
+				}
+				set_caret_line(prev_line, false, true, 0, i);
+				set_caret_column(prev_column, i == 0, i);
+
+				adjust_carets_after_edit(i, prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
+				continue;
 			}
-			set_caret_line(prev_line, false, true);
-			set_caret_column(prev_column);
-			return;
 		}
-	}
 
-	// For space indentation we need to do a simple unindent if there are no chars to the left, acting in the
-	// same way as tabs.
-	if (indent_using_spaces && cc != 0) {
-		if (get_first_non_whitespace_column(cl) >= cc) {
-			prev_column = cc - _calculate_spaces_till_next_left_indent(cc);
-			prev_line = cl;
+		// For space indentation we need to do a simple unindent if there are no chars to the left, acting in the
+		// same way as tabs.
+		if (indent_using_spaces && cc != 0) {
+			if (get_first_non_whitespace_column(cl) >= cc) {
+				prev_column = cc - _calculate_spaces_till_next_left_indent(cc);
+				prev_line = cl;
+			}
 		}
-	}
 
-	remove_text(prev_line, prev_column, cl, cc);
+		remove_text(prev_line, prev_column, cl, cc);
+
+		set_caret_line(prev_line, false, true, 0, i);
+		set_caret_column(prev_column, i == 0, i);
 
-	set_caret_line(prev_line, false, true);
-	set_caret_column(prev_column);
+		adjust_carets_after_edit(i, prev_line, prev_column, cl, cc);
+	}
+	merge_overlapping_carets();
+	end_complex_operation();
 }
 
 /* Indent management */
@@ -803,10 +819,15 @@ void CodeEdit::do_indent() {
 		return;
 	}
 
-	int spaces_to_add = _calculate_spaces_till_next_right_indent(get_caret_column());
-	if (spaces_to_add > 0) {
-		insert_text_at_caret(String(" ").repeat(spaces_to_add));
+	begin_complex_operation();
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &i : caret_edit_order) {
+		int spaces_to_add = _calculate_spaces_till_next_right_indent(get_caret_column(i));
+		if (spaces_to_add > 0) {
+			insert_text_at_caret(String(" ").repeat(spaces_to_add), i);
+		}
 	}
+	end_complex_operation();
 }
 
 void CodeEdit::indent_lines() {
@@ -815,48 +836,49 @@ void CodeEdit::indent_lines() {
 	}
 
 	begin_complex_operation();
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &c : caret_edit_order) {
+		// This value informs us by how much we changed selection position by indenting right.
+		// Default is 1 for tab indentation.
+		int selection_offset = 1;
+
+		int start_line = get_caret_line(c);
+		int end_line = start_line;
+		if (has_selection(c)) {
+			start_line = get_selection_from_line(c);
+			end_line = get_selection_to_line(c);
+
+			// Ignore the last line if the selection is not past the first column.
+			if (get_selection_to_column(c) == 0) {
+				selection_offset = 0;
+				end_line--;
+			}
+		}
 
-	/* This value informs us by how much we changed selection position by indenting right. */
-	/* Default is 1 for tab indentation.                                                   */
-	int selection_offset = 1;
-
-	int start_line = get_caret_line();
-	int end_line = start_line;
-	if (has_selection()) {
-		start_line = get_selection_from_line();
-		end_line = get_selection_to_line();
+		for (int i = start_line; i <= end_line; i++) {
+			const String line_text = get_line(i);
+			if (line_text.size() == 0 && has_selection(c)) {
+				continue;
+			}
 
-		/* Ignore the last line if the selection is not past the first column. */
-		if (get_selection_to_column() == 0) {
-			selection_offset = 0;
-			end_line--;
-		}
-	}
+			if (!indent_using_spaces) {
+				set_line(i, '\t' + line_text);
+				continue;
+			}
 
-	for (int i = start_line; i <= end_line; i++) {
-		const String line_text = get_line(i);
-		if (line_text.size() == 0 && has_selection()) {
-			continue;
+			// We don't really care where selection is - we just need to know indentation level at the beginning of the line.
+			// Since we will add this many spaces, we want to move the whole selection and caret by this much.
+			int spaces_to_add = _calculate_spaces_till_next_right_indent(get_first_non_whitespace_column(i));
+			set_line(i, String(" ").repeat(spaces_to_add) + line_text);
+			selection_offset = spaces_to_add;
 		}
 
-		if (!indent_using_spaces) {
-			set_line(i, '\t' + line_text);
-			continue;
+		// Fix selection and caret being off after shifting selection right.
+		if (has_selection(c)) {
+			select(start_line, get_selection_from_column(c) + selection_offset, get_selection_to_line(c), get_selection_to_column(c) + selection_offset, c);
 		}
-
-		/* We don't really care where selection is - we just need to know indentation level at the beginning of the line. */
-		/* Since we will add this many spaces, we want to move the whole selection and caret by this much.                */
-		int spaces_to_add = _calculate_spaces_till_next_right_indent(get_first_non_whitespace_column(i));
-		set_line(i, String(" ").repeat(spaces_to_add) + line_text);
-		selection_offset = spaces_to_add;
+		set_caret_column(get_caret_column(c) + selection_offset, false, c);
 	}
-
-	/* Fix selection and caret being off after shifting selection right.*/
-	if (has_selection()) {
-		select(start_line, get_selection_from_column() + selection_offset, get_selection_to_line(), get_selection_to_column() + selection_offset);
-	}
-	set_caret_column(get_caret_column() + selection_offset, false);
-
 	end_complex_operation();
 }
 
@@ -872,30 +894,36 @@ void CodeEdit::do_unindent() {
 		return;
 	}
 
-	int cl = get_caret_line();
-	const String &line = get_line(cl);
-
-	if (line[cc - 1] == '\t') {
-		remove_text(cl, cc - 1, cl, cc);
-		set_caret_column(MAX(0, cc - 1));
-		return;
-	}
+	begin_complex_operation();
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &c : caret_edit_order) {
+		int cl = get_caret_line(c);
+		const String &line = get_line(cl);
+
+		if (line[cc - 1] == '\t') {
+			remove_text(cl, cc - 1, cl, cc);
+			set_caret_column(MAX(0, cc - 1), c == 0, c);
+			adjust_carets_after_edit(c, cl, cc, cl, cc - 1);
+			continue;
+		}
 
-	if (line[cc - 1] != ' ') {
-		return;
-	}
+		if (line[cc - 1] != ' ') {
+			continue;
+		}
 
-	int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
-	if (spaces_to_remove > 0) {
-		for (int i = 1; i <= spaces_to_remove; i++) {
-			if (line[cc - i] != ' ') {
-				spaces_to_remove = i - 1;
-				break;
+		int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
+		if (spaces_to_remove > 0) {
+			for (int i = 1; i <= spaces_to_remove; i++) {
+				if (line[cc - i] != ' ') {
+					spaces_to_remove = i - 1;
+					break;
+				}
 			}
+			remove_text(cl, cc - spaces_to_remove, cl, cc);
+			set_caret_column(MAX(0, cc - spaces_to_remove), c == 0, c);
 		}
-		remove_text(cl, cc - spaces_to_remove, cl, cc);
-		set_caret_column(MAX(0, cc - spaces_to_remove));
 	}
+	end_complex_operation();
 }
 
 void CodeEdit::unindent_lines() {
@@ -905,71 +933,73 @@ void CodeEdit::unindent_lines() {
 
 	begin_complex_operation();
 
-	/* Moving caret and selection after unindenting can get tricky because                                                      */
-	/* changing content of line can move caret and selection on its own (if new line ends before previous position of either),  */
-	/* therefore we just remember initial values and at the end of the operation offset them by number of removed characters.   */
-	int removed_characters = 0;
-	int initial_selection_end_column = 0;
-	int initial_cursor_column = get_caret_column();
-
-	int start_line = get_caret_line();
-	int end_line = start_line;
-	if (has_selection()) {
-		start_line = get_selection_from_line();
-		end_line = get_selection_to_line();
-
-		/* Ignore the last line if the selection is not past the first column. */
-		initial_selection_end_column = get_selection_to_column();
-		if (initial_selection_end_column == 0) {
-			end_line--;
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &c : caret_edit_order) {
+		// Moving caret and selection after unindenting can get tricky because
+		// changing content of line can move caret and selection on its own (if new line ends before previous position of either)
+		// therefore we just remember initial values and at the end of the operation offset them by number of removed characters.
+		int removed_characters = 0;
+		int initial_selection_end_column = 0;
+		int initial_cursor_column = get_caret_column(c);
+
+		int start_line = get_caret_line(c);
+		int end_line = start_line;
+		if (has_selection(c)) {
+			start_line = get_selection_from_line(c);
+			end_line = get_selection_to_line(c);
+
+			// Ignore the last line if the selection is not past the first column.
+			initial_selection_end_column = get_selection_to_column(c);
+			if (initial_selection_end_column == 0) {
+				end_line--;
+			}
 		}
-	}
 
-	bool first_line_edited = false;
-	bool last_line_edited = false;
+		bool first_line_edited = false;
+		bool last_line_edited = false;
 
-	for (int i = start_line; i <= end_line; i++) {
-		String line_text = get_line(i);
+		for (int i = start_line; i <= end_line; i++) {
+			String line_text = get_line(i);
 
-		if (line_text.begins_with("\t")) {
-			line_text = line_text.substr(1, line_text.length());
+			if (line_text.begins_with("\t")) {
+				line_text = line_text.substr(1, line_text.length());
 
-			set_line(i, line_text);
-			removed_characters = 1;
+				set_line(i, line_text);
+				removed_characters = 1;
 
-			first_line_edited = (i == start_line) ? true : first_line_edited;
-			last_line_edited = (i == end_line) ? true : last_line_edited;
-			continue;
-		}
+				first_line_edited = (i == start_line) ? true : first_line_edited;
+				last_line_edited = (i == end_line) ? true : last_line_edited;
+				continue;
+			}
 
-		if (line_text.begins_with(" ")) {
-			/* When unindenting we aim to remove spaces before line that has selection no matter what is selected,         */
-			/* Here we remove only enough spaces to align text to nearest full multiple of indentation_size.               */
-			/* In case where selection begins at the start of indentation_size multiple we remove whole indentation level. */
-			int spaces_to_remove = _calculate_spaces_till_next_left_indent(get_first_non_whitespace_column(i));
-			line_text = line_text.substr(spaces_to_remove, line_text.length());
+			if (line_text.begins_with(" ")) {
+				// When unindenting we aim to remove spaces before line that has selection no matter what is selected.
+				// Here we remove only enough spaces to align text to nearest full multiple of indentation_size.
+				// In case where selection begins at the start of indentation_size multiple we remove whole indentation level.
+				int spaces_to_remove = _calculate_spaces_till_next_left_indent(get_first_non_whitespace_column(i));
+				line_text = line_text.substr(spaces_to_remove, line_text.length());
 
-			set_line(i, line_text);
-			removed_characters = spaces_to_remove;
+				set_line(i, line_text);
+				removed_characters = spaces_to_remove;
 
-			first_line_edited = (i == start_line) ? true : first_line_edited;
-			last_line_edited = (i == end_line) ? true : last_line_edited;
+				first_line_edited = (i == start_line) ? true : first_line_edited;
+				last_line_edited = (i == end_line) ? true : last_line_edited;
+			}
 		}
-	}
 
-	if (has_selection()) {
-		/* Fix selection being off by one on the first line. */
-		if (first_line_edited) {
-			select(get_selection_from_line(), get_selection_from_column() - removed_characters, get_selection_to_line(), initial_selection_end_column);
-		}
+		if (has_selection(c)) {
+			// Fix selection being off by one on the first line.
+			if (first_line_edited) {
+				select(get_selection_from_line(c), get_selection_from_column(c) - removed_characters, get_selection_to_line(c), initial_selection_end_column, c);
+			}
 
-		/* Fix selection being off by one on the last line. */
-		if (last_line_edited) {
-			select(get_selection_from_line(), get_selection_from_column(), get_selection_to_line(), initial_selection_end_column - removed_characters);
+			// Fix selection being off by one on the last line.
+			if (last_line_edited) {
+				select(get_selection_from_line(c), get_selection_from_column(c), get_selection_to_line(c), initial_selection_end_column - removed_characters, c);
+			}
 		}
+		set_caret_column(initial_cursor_column - removed_characters, false, c);
 	}
-	set_caret_column(initial_cursor_column - removed_characters, false);
-
 	end_complex_operation();
 }
 
@@ -990,106 +1020,108 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
 		return;
 	}
 
-	/* When not splitting the line, we need to factor in indentation from the end of the current line. */
-	const int cc = p_split_current_line ? get_caret_column() : get_line(get_caret_line()).length();
-	const int cl = get_caret_line();
-
-	const String line = get_line(cl);
-
-	String ins = "\n";
+	begin_complex_operation();
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &i : caret_edit_order) {
+		// When not splitting the line, we need to factor in indentation from the end of the current line.
+		const int cc = p_split_current_line ? get_caret_column(i) : get_line(get_caret_line(i)).length();
+		const int cl = get_caret_line(i);
 
-	/* Append current indentation. */
-	int space_count = 0;
-	int line_col = 0;
-	for (; line_col < cc; line_col++) {
-		if (line[line_col] == '\t') {
-			ins += indent_text;
-			space_count = 0;
-			continue;
-		}
+		const String line = get_line(cl);
 
-		if (line[line_col] == ' ') {
-			space_count++;
+		String ins = "\n";
 
-			if (space_count == indent_size) {
+		// Append current indentation.
+		int space_count = 0;
+		int line_col = 0;
+		for (; line_col < cc; line_col++) {
+			if (line[line_col] == '\t') {
 				ins += indent_text;
 				space_count = 0;
+				continue;
 			}
-			continue;
-		}
-		break;
-	}
-
-	if (is_line_folded(cl)) {
-		unfold_line(cl);
-	}
 
-	/* Indent once again if the previous line needs it, ie ':'.          */
-	/* Then add an addition new line for any closing pairs aka '()'.     */
-	/* Skip this in comments or if we are going above.                   */
-	bool brace_indent = false;
-	if (auto_indent && !p_above && cc > 0 && is_in_comment(cl) == -1) {
-		bool should_indent = false;
-		char32_t indent_char = ' ';
+			if (line[line_col] == ' ') {
+				space_count++;
 
-		for (; line_col < cc; line_col++) {
-			char32_t c = line[line_col];
-			if (auto_indent_prefixes.has(c)) {
-				should_indent = true;
-				indent_char = c;
+				if (space_count == indent_size) {
+					ins += indent_text;
+					space_count = 0;
+				}
 				continue;
 			}
+			break;
+		}
 
-			/* Make sure this is the last char, trailing whitespace or comments are okay. */
-			/* Increment column for comments because the delimiter (#) should be ignored. */
-			if (should_indent && (!is_whitespace(c) && is_in_comment(cl, line_col + 1) == -1)) {
-				should_indent = false;
-			}
+		if (is_line_folded(cl)) {
+			unfold_line(cl);
 		}
 
-		if (should_indent) {
-			ins += indent_text;
+		// Indent once again if the previous line needs it, ie ':'.
+		// Then add an addition new line for any closing pairs aka '()'.
+		// Skip this in comments or if we are going above.
+		bool brace_indent = false;
+		if (auto_indent && !p_above && cc > 0 && is_in_comment(cl) == -1) {
+			bool should_indent = false;
+			char32_t indent_char = ' ';
 
-			String closing_pair = get_auto_brace_completion_close_key(String::chr(indent_char));
-			if (!closing_pair.is_empty() && line.find(closing_pair, cc) == cc) {
-				/* No need to move the brace below if we are not taking the text with us. */
-				if (p_split_current_line) {
-					brace_indent = true;
-					ins += "\n" + ins.substr(indent_text.size(), ins.length() - 2);
-				} else {
-					brace_indent = false;
-					ins = "\n" + ins.substr(indent_text.size(), ins.length() - 2);
+			for (; line_col < cc; line_col++) {
+				char32_t c = line[line_col];
+				if (auto_indent_prefixes.has(c)) {
+					should_indent = true;
+					indent_char = c;
+					continue;
+				}
+
+				// Make sure this is the last char, trailing whitespace or comments are okay.
+				// Increment column for comments because the delimiter (#) should be ignored.
+				if (should_indent && (!is_whitespace(c) && is_in_comment(cl, line_col + 1) == -1)) {
+					should_indent = false;
 				}
 			}
-		}
-	}
 
-	begin_complex_operation();
+			if (should_indent) {
+				ins += indent_text;
+
+				String closing_pair = get_auto_brace_completion_close_key(String::chr(indent_char));
+				if (!closing_pair.is_empty() && line.find(closing_pair, cc) == cc) {
+					// No need to move the brace below if we are not taking the text with us.
+					if (p_split_current_line) {
+						brace_indent = true;
+						ins += "\n" + ins.substr(indent_text.size(), ins.length() - 2);
+					} else {
+						brace_indent = false;
+						ins = "\n" + ins.substr(indent_text.size(), ins.length() - 2);
+					}
+				}
+			}
+		}
 
-	bool first_line = false;
-	if (!p_split_current_line) {
-		deselect();
+		bool first_line = false;
+		if (!p_split_current_line) {
+			deselect(i);
 
-		if (p_above) {
-			if (cl > 0) {
-				set_caret_line(cl - 1, false);
-				set_caret_column(get_line(get_caret_line()).length());
+			if (p_above) {
+				if (cl > 0) {
+					set_caret_line(cl - 1, false, true, 0, i);
+					set_caret_column(get_line(get_caret_line(i)).length(), i == 0, i);
+				} else {
+					set_caret_column(0, i == 0, i);
+					first_line = true;
+				}
 			} else {
-				set_caret_column(0);
-				first_line = true;
+				set_caret_column(line.length(), i == 0, i);
 			}
-		} else {
-			set_caret_column(line.length());
 		}
-	}
 
-	insert_text_at_caret(ins);
+		insert_text_at_caret(ins, i);
 
-	if (first_line) {
-		set_caret_line(0);
-	} else if (brace_indent) {
-		set_caret_line(get_caret_line() - 1, false);
-		set_caret_column(get_line(get_caret_line()).length());
+		if (first_line) {
+			set_caret_line(0, i == 0, true, 0, i);
+		} else if (brace_indent) {
+			set_caret_line(get_caret_line(i) - 1, false, true, 0, i);
+			set_caret_column(get_line(get_caret_line(i)).length(), i == 0, i);
+		}
 	}
 
 	end_complex_operation();
@@ -1522,22 +1554,26 @@ void CodeEdit::fold_line(int p_line) {
 		_set_line_as_hidden(i, true);
 	}
 
-	/* Fix selection. */
-	if (has_selection()) {
-		if (_is_line_hidden(get_selection_from_line()) && _is_line_hidden(get_selection_to_line())) {
-			deselect();
-		} else if (_is_line_hidden(get_selection_from_line())) {
-			select(p_line, 9999, get_selection_to_line(), get_selection_to_column());
-		} else if (_is_line_hidden(get_selection_to_line())) {
-			select(get_selection_from_line(), get_selection_from_column(), p_line, 9999);
+	for (int i = 0; i < get_caret_count(); i++) {
+		// Fix selection.
+		if (has_selection(i)) {
+			if (_is_line_hidden(get_selection_from_line(i)) && _is_line_hidden(get_selection_to_line(i))) {
+				deselect(i);
+			} else if (_is_line_hidden(get_selection_from_line(i))) {
+				select(p_line, 9999, get_selection_to_line(i), get_selection_to_column(i), i);
+			} else if (_is_line_hidden(get_selection_to_line(i))) {
+				select(get_selection_from_line(i), get_selection_from_column(i), p_line, 9999, i);
+			}
 		}
-	}
 
-	/* Reset caret. */
-	if (_is_line_hidden(get_caret_line())) {
-		set_caret_line(p_line, false, false);
-		set_caret_column(get_line(p_line).length(), false);
+		// Reset caret.
+		if (_is_line_hidden(get_caret_line(i))) {
+			set_caret_line(p_line, false, false, 0, i);
+			set_caret_column(get_line(p_line).length(), false, i);
+		}
 	}
+
+	merge_overlapping_carets();
 	queue_redraw();
 }
 
@@ -1950,98 +1986,108 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
 		return;
 	}
 
+	char32_t caret_last_completion_char;
 	begin_complex_operation();
+	Vector<int> caret_edit_order = get_caret_index_edit_order();
+	for (const int &i : caret_edit_order) {
+		int caret_line = get_caret_line(i);
+
+		const String &insert_text = code_completion_options[code_completion_current_selected].insert_text;
+		const String &display_text = code_completion_options[code_completion_current_selected].display;
+
+		if (p_replace) {
+			// Find end of current section.
+			const String line = get_line(caret_line);
+			int caret_col = get_caret_column(i);
+			int caret_remove_line = caret_line;
+
+			bool merge_text = true;
+			int in_string = is_in_string(caret_line, caret_col);
+			if (in_string != -1) {
+				Point2 string_end = get_delimiter_end_position(caret_line, caret_col);
+				if (string_end.x != -1) {
+					merge_text = false;
+					caret_remove_line = string_end.y;
+					caret_col = string_end.x - 1;
+				}
+			}
 
-	int caret_line = get_caret_line();
-
-	const String &insert_text = code_completion_options[code_completion_current_selected].insert_text;
-	const String &display_text = code_completion_options[code_completion_current_selected].display;
-
-	if (p_replace) {
-		/* Find end of current section */
-		const String line = get_line(caret_line);
-		int caret_col = get_caret_column();
-		int caret_remove_line = caret_line;
-
-		bool merge_text = true;
-		int in_string = is_in_string(caret_line, caret_col);
-		if (in_string != -1) {
-			Point2 string_end = get_delimiter_end_position(caret_line, caret_col);
-			if (string_end.x != -1) {
-				merge_text = false;
-				caret_remove_line = string_end.y;
-				caret_col = string_end.x - 1;
+			if (merge_text) {
+				for (; caret_col < line.length(); caret_col++) {
+					if (is_symbol(line[caret_col])) {
+						break;
+					}
+				}
 			}
-		}
 
-		if (merge_text) {
-			for (; caret_col < line.length(); caret_col++) {
-				if (is_symbol(line[caret_col])) {
+			// Replace.
+			remove_text(caret_line, get_caret_column(i) - code_completion_base.length(), caret_remove_line, caret_col);
+			adjust_carets_after_edit(i, caret_line, caret_col - code_completion_base.length(), caret_remove_line, caret_col);
+			set_caret_column(get_caret_column(i) - code_completion_base.length(), false, i);
+			insert_text_at_caret(insert_text, i);
+		} else {
+			// Get first non-matching char.
+			const String line = get_line(caret_line);
+			int caret_col = get_caret_column(i);
+			int matching_chars = code_completion_base.length();
+			for (; matching_chars <= insert_text.length(); matching_chars++) {
+				if (caret_col >= line.length() || line[caret_col] != insert_text[matching_chars]) {
 					break;
 				}
+				caret_col++;
 			}
+
+			// Remove base completion text.
+			remove_text(caret_line, get_caret_column(i) - code_completion_base.length(), caret_line, get_caret_column(i));
+			adjust_carets_after_edit(i, caret_line, get_caret_column(i) - code_completion_base.length(), caret_line, get_caret_column(i));
+			set_caret_column(get_caret_column(i) - code_completion_base.length(), false, i);
+
+			// Merge with text.
+			insert_text_at_caret(insert_text.substr(0, code_completion_base.length()), i);
+			set_caret_column(caret_col, false, i);
+			insert_text_at_caret(insert_text.substr(matching_chars), i);
 		}
 
-		/* Replace. */
-		remove_text(caret_line, get_caret_column() - code_completion_base.length(), caret_remove_line, caret_col);
-		set_caret_column(get_caret_column() - code_completion_base.length(), false);
-		insert_text_at_caret(insert_text);
-	} else {
-		/* Get first non-matching char. */
+		//* Handle merging of symbols eg strings, brackets.
 		const String line = get_line(caret_line);
-		int caret_col = get_caret_column();
-		int matching_chars = code_completion_base.length();
-		for (; matching_chars <= insert_text.length(); matching_chars++) {
-			if (caret_col >= line.length() || line[caret_col] != insert_text[matching_chars]) {
-				break;
-			}
-			caret_col++;
+		char32_t next_char = line[get_caret_column(i)];
+		char32_t last_completion_char = insert_text[insert_text.length() - 1];
+		if (i == 0) {
+			caret_last_completion_char = last_completion_char;
 		}
+		char32_t last_completion_char_display = display_text[display_text.length() - 1];
 
-		/* Remove base completion text. */
-		remove_text(caret_line, get_caret_column() - code_completion_base.length(), caret_line, get_caret_column());
-		set_caret_column(get_caret_column() - code_completion_base.length(), false);
-
-		/* Merge with text. */
-		insert_text_at_caret(insert_text.substr(0, code_completion_base.length()));
-		set_caret_column(caret_col, false);
-		insert_text_at_caret(insert_text.substr(matching_chars));
-	}
-
-	/* Handle merging of symbols eg strings, brackets. */
-	const String line = get_line(caret_line);
-	char32_t next_char = line[get_caret_column()];
-	char32_t last_completion_char = insert_text[insert_text.length() - 1];
-	char32_t last_completion_char_display = display_text[display_text.length() - 1];
+		int pre_brace_pair = get_caret_column(i) > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column(i)) : -1;
+		int post_brace_pair = get_caret_column(i) < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i)) : -1;
 
-	int pre_brace_pair = get_caret_column() > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column()) : -1;
-	int post_brace_pair = get_caret_column() < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column()) : -1;
-
-	if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) {
-		remove_text(caret_line, get_caret_column(), caret_line, get_caret_column() + 1);
-	}
+		if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) {
+			remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+			adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+		}
 
-	if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) {
-		remove_text(caret_line, get_caret_column(), caret_line, get_caret_column() + 1);
-	} else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) {
-		insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
-		set_caret_column(get_caret_column() - auto_brace_completion_pairs[pre_brace_pair].close_key.length());
-	}
+		if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) {
+			remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+			adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+		} else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) {
+			insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key, i);
+			set_caret_column(get_caret_column(i) - auto_brace_completion_pairs[pre_brace_pair].close_key.length(), i == 0, i);
+		}
 
-	if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column() > 0 && get_caret_column() < get_line(caret_line).length()) {
-		pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column() + 1);
-		if (pre_brace_pair != -1 && pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) {
-			remove_text(caret_line, get_caret_column() - 2, caret_line, get_caret_column());
-			if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1) != pre_brace_pair) {
-				set_caret_column(get_caret_column() - 1);
+		if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column(i) > 0 && get_caret_column(i) < get_line(caret_line).length()) {
+			pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column(i) + 1);
+			if (pre_brace_pair != -1 && pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i) - 1)) {
+				remove_text(caret_line, get_caret_column(i) - 2, caret_line, get_caret_column(i));
+				adjust_carets_after_edit(i, caret_line, get_caret_column(i) - 2, caret_line, get_caret_column(i));
+				if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i) - 1) != pre_brace_pair) {
+					set_caret_column(get_caret_column(i) - 1, i == 0, i);
+				}
 			}
 		}
 	}
-
 	end_complex_operation();
 
 	cancel_code_completion();
-	if (code_completion_prefixes.has(last_completion_char)) {
+	if (code_completion_prefixes.has(caret_last_completion_char)) {
 		request_code_completion();
 	}
 }
@@ -2399,6 +2445,7 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
 	}
 
 	if (p_gutter == line_number_gutter) {
+		remove_secondary_carets();
 		set_selection_mode(TextEdit::SelectionMode::SELECTION_MODE_LINE, p_line, 0);
 		select(p_line, 0, p_line + 1, 0);
 		set_caret_line(p_line + 1);