2
0
Эх сурвалжийг харах

Merge pull request #28811 from iwek7/editor_remove_spaces_to_closes_indention

Change rules of indenting for spaces
Rémi Verschelde 6 жил өмнө
parent
commit
f173d242a2

+ 81 - 44
scene/gui/text_edit.cpp

@@ -104,6 +104,13 @@ static CharType _get_right_pair_symbol(CharType c) {
 	return 0;
 }
 
+static int _find_first_non_whitespace_column_of_line(const String &line) {
+	int left = 0;
+	while (left < line.length() && _is_whitespace(line[left]))
+		left++;
+	return left;
+}
+
 void TextEdit::Text::set_font(const Ref<Font> &p_font) {
 
 	font = p_font;
@@ -1645,31 +1652,25 @@ void TextEdit::backspace_at_cursor() {
 		_consume_backspace_for_pair_symbol(prev_line, prev_column);
 	} else {
 		// handle space indentation
-		if (cursor.column - indent_size >= 0 && indent_using_spaces) {
-
-			// if there is enough spaces to count as a tab
+		if (cursor.column != 0 && indent_using_spaces) {
+			// check if there are no other chars before cursor, just indentation
 			bool unindent = true;
-			for (int i = 1; i <= indent_size; i++) {
-				if (text[cursor.line][cursor.column - i] != ' ') {
-					unindent = false;
-					break;
-				}
-			}
-
-			// and it is before the first character
 			int i = 0;
 			while (i < cursor.column && i < text[cursor.line].length()) {
-				if (text[cursor.line][i] != ' ' && text[cursor.line][i] != '\t') {
+				if (!_is_whitespace(text[cursor.line][i])) {
 					unindent = false;
 					break;
 				}
 				i++;
 			}
 
-			// then we can remove it as a single character.
+			// then we can remove all spaces as a single character.
 			if (unindent) {
-				_remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column);
-				prev_column = cursor.column - indent_size;
+				// we want to remove spaces up to closest indent
+				// or whole indent if cursor is pointing at it
+				int spaces_to_delete = _calculate_spaces_till_next_left_indent(cursor.column);
+				prev_column = cursor.column - spaces_to_delete;
+				_remove_text(cursor.line, prev_column, cursor.line, cursor.column);
 			} else {
 				_remove_text(prev_line, prev_column, cursor.line, cursor.column);
 			}
@@ -1686,6 +1687,10 @@ void TextEdit::indent_right() {
 
 	int start_line;
 	int 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;
 	begin_complex_operation();
 
 	if (is_selection_active()) {
@@ -1704,18 +1709,24 @@ void TextEdit::indent_right() {
 	for (int i = start_line; i <= end_line; i++) {
 		String line_text = get_line(i);
 		if (indent_using_spaces) {
-			line_text = space_indent + line_text;
+			// we don't really care where selection is - we just need to know indentation level at the beginning of the line
+			int left = _find_first_non_whitespace_column_of_line(line_text);
+			int spaces_to_add = _calculate_spaces_till_next_right_indent(left);
+			// since we will add this much spaces we want move whole selection and cursor by this much
+			selection_offset = spaces_to_add;
+			for (int j = 0; j < spaces_to_add; j++)
+				line_text = ' ' + line_text;
 		} else {
 			line_text = '\t' + line_text;
 		}
 		set_line(i, line_text);
 	}
 
-	// fix selection and cursor being off by one on the last line
+	// fix selection and cursor being off after shifting selection right
 	if (is_selection_active()) {
-		select(selection.from_line, selection.from_column + 1, selection.to_line, selection.to_column + 1);
+		select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset);
 	}
-	cursor_set_column(cursor.column + 1, false);
+	cursor_set_column(cursor.column + selection_offset, false);
 	end_complex_operation();
 	update();
 }
@@ -1724,6 +1735,15 @@ void TextEdit::indent_left() {
 
 	int start_line;
 	int end_line;
+
+	// moving cursor and selection after unindenting can get tricky
+	// because changing content of line can move cursor and selection on it's 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 = selection.to_column;
+	int initial_cursor_column = cursor.column;
+
 	begin_complex_operation();
 
 	if (is_selection_active()) {
@@ -1746,21 +1766,43 @@ void TextEdit::indent_left() {
 		if (line_text.begins_with("\t")) {
 			line_text = line_text.substr(1, line_text.length());
 			set_line(i, line_text);
-		} else if (line_text.begins_with(space_indent)) {
-			line_text = line_text.substr(indent_size, line_text.length());
+			removed_characters = 1;
+		} else if (line_text.begins_with(" ")) {
+			// when unindenting we aim to remove spaces before line that has selection no matter what is selected
+			// so we start of by finding first non whitespace character of line
+			int left = _find_first_non_whitespace_column_of_line(line_text);
+
+			// 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(left);
+
+			line_text = line_text.substr(spaces_to_remove, line_text.length());
 			set_line(i, line_text);
+			removed_characters = spaces_to_remove;
 		}
 	}
 
 	// fix selection and cursor being off by one on the last line
 	if (is_selection_active() && last_line_text != get_line(end_line)) {
-		select(selection.from_line, selection.from_column - 1, selection.to_line, selection.to_column - 1);
+		select(selection.from_line, selection.from_column - removed_characters,
+				selection.to_line, initial_selection_end_column - removed_characters);
 	}
-	cursor_set_column(cursor.column - 1, false);
+	cursor_set_column(initial_cursor_column - removed_characters, false);
 	end_complex_operation();
 	update();
 }
 
+int TextEdit::_calculate_spaces_till_next_left_indent(int column) {
+	int spaces_till_indent = column % indent_size;
+	if (spaces_till_indent == 0)
+		spaces_till_indent = indent_size;
+	return spaces_till_indent;
+}
+
+int TextEdit::_calculate_spaces_till_next_right_indent(int column) {
+	return indent_size - column % indent_size;
+}
+
 void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const {
 
 	float rows = p_mouse.y;
@@ -2517,15 +2559,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
 				} else {
 					if (k->get_shift()) {
 
-						//simple unindent
+						// simple unindent
 						int cc = cursor.column;
-
-						const int len = text[cursor.line].length();
 						const String &line = text[cursor.line];
 
-						int left = 0; // number of whitespace chars at beginning of line
-						while (left < len && (line[left] == '\t' || line[left] == ' '))
-							left++;
+						int left = _find_first_non_whitespace_column_of_line(line);
 						cc = MIN(cc, left);
 
 						while (cc < indent_size && cc < left && line[cc] == ' ')
@@ -2533,24 +2571,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
 
 						if (cc > 0 && cc <= text[cursor.line].length()) {
 							if (text[cursor.line][cc - 1] == '\t') {
+								// tabs unindentation
 								_remove_text(cursor.line, cc - 1, cursor.line, cc);
 								if (cursor.column >= left)
 									cursor_set_column(MAX(0, cursor.column - 1));
 								update();
 							} else {
-								int n = 0;
-
-								for (int i = 1; i <= MIN(cc, indent_size); i++) {
-									if (line[cc - i] != ' ') {
-										break;
-									}
-									n++;
-								}
-
-								if (n > 0) {
-									_remove_text(cursor.line, cc - n, cursor.line, cc);
-									if (cursor.column > left - n) // inside text?
-										cursor_set_column(MAX(0, cursor.column - n));
+								// spaces unindentation
+								int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
+								if (spaces_to_remove > 0) {
+									_remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc);
+									if (cursor.column > left - spaces_to_remove) // inside text?
+										cursor_set_column(MAX(0, cursor.column - spaces_to_remove));
 									update();
 								}
 							}
@@ -2559,9 +2591,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
 							update();
 						}
 					} else {
-						//simple indent
+						// simple indent
 						if (indent_using_spaces) {
-							_insert_text_at_cursor(space_indent);
+							// insert only as much spaces as needed till next indentation level
+							int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column);
+							String indent_to_insert = String();
+							for (int i = 0; i < spaces_to_add; i++)
+								indent_to_insert = ' ' + indent_to_insert;
+							_insert_text_at_cursor(indent_to_insert);
 						} else {
 							_insert_text_at_cursor("\t");
 						}

+ 3 - 0
scene/gui/text_edit.h

@@ -437,6 +437,9 @@ private:
 	void _confirm_completion();
 	void _update_completion_candidates();
 
+	int _calculate_spaces_till_next_left_indent(int column);
+	int _calculate_spaces_till_next_right_indent(int column);
+
 protected:
 	virtual String get_tooltip(const Point2 &p_pos) const;