Selaa lähdekoodia

Merge pull request #64564 from timothyqiu/word-wrap-3.x

[3.x] Fix `String::word_wrap()` for long words
Rémi Verschelde 2 vuotta sitten
vanhempi
commit
4769aa4499
2 muutettua tiedostoa jossa 87 lisäystä ja 21 poistoa
  1. 51 21
      core/ustring.cpp
  2. 36 0
      main/tests/test_string.cpp

+ 51 - 21
core/ustring.cpp

@@ -3428,33 +3428,63 @@ bool String::is_valid_identifier() const {
 	return true;
 }
 
-//kind of poor should be rewritten properly
-
 String String::word_wrap(int p_chars_per_line) const {
-	int from = 0;
-	int last_space = 0;
 	String ret;
+
+	int line_start = 0;
+	int line_end = 0; // End of last word on current line.
+	int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
+	int word_length = 0;
+
 	for (int i = 0; i < length(); i++) {
-		if (i - from >= p_chars_per_line) {
-			if (last_space == -1) {
-				ret += substr(from, i - from + 1) + "\n";
-			} else {
-				ret += substr(from, last_space - from) + "\n";
-				i = last_space; //rewind
-			}
-			from = i + 1;
-			last_space = -1;
-		} else if (operator[](i) == ' ' || operator[](i) == '\t') {
-			last_space = i;
-		} else if (operator[](i) == '\n') {
-			ret += substr(from, i - from) + "\n";
-			from = i + 1;
-			last_space = -1;
+		const CharType c = operator[](i);
+
+		switch (c) {
+			case '\n': {
+				// Force newline.
+				ret += substr(line_start, i - line_start + 1);
+				line_start = i + 1;
+				line_end = line_start;
+				word_start = line_start;
+				word_length = 0;
+			} break;
+
+			case ' ':
+			case '\t': {
+				// A whitespace ends current word.
+				if (word_length > 0) {
+					line_end = i - 1;
+					word_start = -1;
+					word_length = 0;
+				}
+			} break;
+
+			default: {
+				if (word_start == -1) {
+					word_start = i;
+				}
+				word_length += 1;
+
+				if (word_length > p_chars_per_line) {
+					// Word too long: wrap before current character.
+					ret += substr(line_start, i - line_start) + "\n";
+					line_start = i;
+					line_end = i;
+					word_start = i;
+					word_length = 1;
+				} else if (i - line_start + 1 > p_chars_per_line) {
+					// Line too long: wrap after the last word.
+					ret += substr(line_start, line_end - line_start + 1) + "\n";
+					line_start = word_start;
+					line_end = line_start;
+				}
+			} break;
 		}
 	}
 
-	if (from < length()) {
-		ret += substr(from, length());
+	const int remaining = length() - line_start;
+	if (remaining) {
+		ret += substr(line_start, remaining);
 	}
 
 	return ret;

+ 36 - 0
main/tests/test_string.cpp

@@ -1219,6 +1219,41 @@ bool test_36() {
 	return true;
 }
 
+bool test_37() {
+#define CHECK_EQ(X, Y)                                            \
+	if ((X) != (Y)) {                                             \
+		OS::get_singleton()->print("\tFAIL: %s != %s\n", #X, #Y); \
+		return false;                                             \
+	} else {                                                      \
+		OS::get_singleton()->print("\tPASS\n");                   \
+	}
+	OS::get_singleton()->print("\n\nTest 37: Word wrap\n");
+
+	// Long words.
+	CHECK_EQ(String("12345678").word_wrap(8), "12345678");
+	CHECK_EQ(String("1234567812345678").word_wrap(8), "12345678\n12345678");
+	CHECK_EQ(String("123456781234567812345678").word_wrap(8), "12345678\n12345678\n12345678");
+
+	// Long line.
+	CHECK_EQ(String("123 567 123456 123").word_wrap(8), "123 567\n123456\n123");
+
+	// Force newline at line length should not create another newline.
+	CHECK_EQ(String("12345678 123").word_wrap(8), "12345678\n123");
+	CHECK_EQ(String("12345678\n123").word_wrap(8), "12345678\n123");
+
+	// Wrapping removes spaces.
+	CHECK_EQ(String("1234567   123").word_wrap(8), "1234567\n123");
+	CHECK_EQ(String("12345678  123").word_wrap(8), "12345678\n123");
+
+	// Wrapping does not remove leading space.
+	CHECK_EQ(String("  123456   123   12").word_wrap(8), "  123456\n123   12");
+	CHECK_EQ(String("  123456\n   456   12").word_wrap(8), "  123456\n   456\n12");
+	CHECK_EQ(String("  123456\n   4  12345678").word_wrap(8), "  123456\n   4\n12345678");
+	CHECK_EQ(String("  123456\n   4  12345678123").word_wrap(8), "  123456\n   4\n12345678\n123");
+
+	return true;
+}
+
 typedef bool (*TestFunc)();
 
 TestFunc test_funcs[] = {
@@ -1259,6 +1294,7 @@ TestFunc test_funcs[] = {
 	test_34,
 	test_35,
 	test_36,
+	test_37,
 	nullptr
 
 };