diff --git a/core/ustring.cpp b/core/ustring.cpp index bcb35a8baae..3cf9818861e 100644 --- a/core/ustring.cpp +++ b/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; diff --git a/main/tests/test_string.cpp b/main/tests/test_string.cpp index 162b677bc9d..543c9c43456 100644 --- a/main/tests/test_string.cpp +++ b/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 };