Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarley Acheson <harley.acheson@gmail.com>2022-09-27 18:39:24 +0300
committerHarley Acheson <harley.acheson@gmail.com>2022-09-27 18:40:36 +0300
commit12fdf9069abe3cd2250a9efec6e059eb85ec59d8 (patch)
treea937501d90a2fd99e5ec63d4d99e6337fc4067ea /source/blender/blenlib
parent697e86a76199c66267370f0222932b8fcb30dc3d (diff)
BLF: Editing Text with Combining Characters
Corrections for caret insertion & movement and deletion for text strings that include non-precomposed diacritical marks (Unicode combining characters). See D15659 for more details and examples. Differential Revision: https://developer.blender.org/D15659 Reviewed by Campbell Barton
Diffstat (limited to 'source/blender/blenlib')
-rw-r--r--source/blender/blenlib/BLI_string_cursor_utf8.h3
-rw-r--r--source/blender/blenlib/intern/string_cursor_utf8.c59
-rw-r--r--source/blender/blenlib/tests/BLI_string_utf8_test.cc483
3 files changed, 521 insertions, 24 deletions
diff --git a/source/blender/blenlib/BLI_string_cursor_utf8.h b/source/blender/blenlib/BLI_string_cursor_utf8.h
index 70ba5da8e5a..9c0589b230a 100644
--- a/source/blender/blenlib/BLI_string_cursor_utf8.h
+++ b/source/blender/blenlib/BLI_string_cursor_utf8.h
@@ -25,6 +25,9 @@ typedef enum eStrCursorJumpDirection {
bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos);
bool BLI_str_cursor_step_prev_utf8(const char *str, size_t maxlen, int *pos);
+bool BLI_str_cursor_step_next_utf32(const char32_t *str, size_t maxlen, int *pos);
+bool BLI_str_cursor_step_prev_utf32(const char32_t *str, size_t maxlen, int *pos);
+
void BLI_str_cursor_step_utf8(const char *str,
size_t maxlen,
int *pos,
diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c
index 6b7151be969..2405b134428 100644
--- a/source/blender/blenlib/intern/string_cursor_utf8.c
+++ b/source/blender/blenlib/intern/string_cursor_utf8.c
@@ -96,27 +96,35 @@ static eStrCursorDelimType cursor_delim_type_utf8(const char *ch_utf8,
return cursor_delim_type_unicode(uch);
}
+/* Keep in sync with BLI_str_cursor_step_next_utf32. */
bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos)
{
+ if ((*pos) >= (int)maxlen) {
+ return false;
+ }
const char *str_end = str + (maxlen + 1);
const char *str_pos = str + (*pos);
- const char *str_next = BLI_str_find_next_char_utf8(str_pos, str_end);
- if (str_next != str_end) {
- (*pos) += (str_next - str_pos);
- if ((*pos) > (int)maxlen) {
- (*pos) = (int)maxlen;
- }
- return true;
+ const char *str_next = str_pos;
+ do {
+ str_next = BLI_str_find_next_char_utf8(str_next, str_end);
+ } while (str_next < str_end && str_next[0] != 0 && BLI_str_utf8_char_width(str_next) < 1);
+ (*pos) += (str_next - str_pos);
+ if ((*pos) > (int)maxlen) {
+ (*pos) = (int)maxlen;
}
- return false;
+ return true;
}
-bool BLI_str_cursor_step_prev_utf8(const char *str, size_t UNUSED(maxlen), int *pos)
+/* Keep in sync with BLI_str_cursor_step_prev_utf32. */
+bool BLI_str_cursor_step_prev_utf8(const char *str, size_t maxlen, int *pos)
{
- if ((*pos) > 0) {
+ if ((*pos) > 0 && (*pos) <= maxlen) {
const char *str_pos = str + (*pos);
- const char *str_prev = BLI_str_find_prev_char_utf8(str_pos, str);
+ const char *str_prev = str_pos;
+ do {
+ str_prev = BLI_str_find_prev_char_utf8(str_prev, str);
+ } while (str_prev > str && BLI_str_utf8_char_width(str_prev) == 0);
(*pos) -= (str_pos - str_prev);
return true;
}
@@ -202,26 +210,29 @@ void BLI_str_cursor_step_utf8(const char *str,
}
}
-/* UTF32 version of BLI_str_cursor_step_utf8 (keep in sync!)
- * less complex since it doesn't need to do multi-byte stepping.
- */
-
-/* Helper functions so we can match #BLI_str_cursor_step_utf8. */
-static bool cursor_step_next_utf32(const char32_t *UNUSED(str), size_t maxlen, int *pos)
+/* Keep in sync with BLI_str_cursor_step_next_utf8. */
+bool BLI_str_cursor_step_next_utf32(const char32_t *str, size_t maxlen, int *pos)
{
if ((*pos) >= (int)maxlen) {
return false;
}
- (*pos)++;
+ do {
+ (*pos)++;
+ } while (*pos < (int)maxlen && str[*pos] != 0 && BLI_wcwidth(str[*pos]) == 0);
+
return true;
}
-static bool cursor_step_prev_utf32(const char32_t *UNUSED(str), size_t UNUSED(maxlen), int *pos)
+/* Keep in sync with BLI_str_cursor_step_prev_utf8. */
+bool BLI_str_cursor_step_prev_utf32(const char32_t *str, size_t UNUSED(maxlen), int *pos)
{
if ((*pos) <= 0) {
return false;
}
- (*pos)--;
+ do {
+ (*pos)--;
+ } while (*pos > 0 && BLI_wcwidth(str[*pos]) == 0);
+
return true;
}
@@ -236,7 +247,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
if (direction == STRCUR_DIR_NEXT) {
if (use_init_step) {
- cursor_step_next_utf32(str, maxlen, pos);
+ BLI_str_cursor_step_next_utf32(str, maxlen, pos);
}
else {
BLI_assert(jump == STRCUR_JUMP_DELIM);
@@ -250,7 +261,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
* look at function cursor_delim_type_unicode() for complete
* list of special character, ctr -> */
while ((*pos) < maxlen) {
- if (cursor_step_next_utf32(str, maxlen, pos)) {
+ if (BLI_str_cursor_step_next_utf32(str, maxlen, pos)) {
if ((jump != STRCUR_JUMP_ALL) &&
(delim_type != cursor_delim_type_unicode((uint)str[*pos]))) {
break;
@@ -264,7 +275,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
}
else if (direction == STRCUR_DIR_PREV) {
if (use_init_step) {
- cursor_step_prev_utf32(str, maxlen, pos);
+ BLI_str_cursor_step_prev_utf32(str, maxlen, pos);
}
else {
BLI_assert(jump == STRCUR_JUMP_DELIM);
@@ -279,7 +290,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
* list of special character, ctr -> */
while ((*pos) > 0) {
const int pos_prev = *pos;
- if (cursor_step_prev_utf32(str, maxlen, pos)) {
+ if (BLI_str_cursor_step_prev_utf32(str, maxlen, pos)) {
if ((jump != STRCUR_JUMP_ALL) &&
(delim_type != cursor_delim_type_unicode((uint)str[*pos]))) {
/* left only: compensate for index/change in direction */
diff --git a/source/blender/blenlib/tests/BLI_string_utf8_test.cc b/source/blender/blenlib/tests/BLI_string_utf8_test.cc
index 7179eef5adb..f0c34350e1b 100644
--- a/source/blender/blenlib/tests/BLI_string_utf8_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_utf8_test.cc
@@ -4,6 +4,7 @@
#include "BLI_rand.h"
#include "BLI_string.h"
+#include "BLI_string_cursor_utf8.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
@@ -393,3 +394,485 @@ TEST(string, Utf8AsUnicodeStep)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_empty
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Empty)
+{
+ const char32_t empty[] = U"";
+ const size_t len = 0;
+ int pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(empty, len, &pos));
+ pos = 1;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(empty, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_single
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Single)
+
+{
+ const char32_t single[] = U"0";
+ const size_t len = 1;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(single, len, &pos) && pos == 1);
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(single, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_simple
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Simple)
+{
+ const char32_t simple[] = U"012";
+ const size_t len = 3;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(simple, len, &pos) && pos == 1);
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(simple, len, &pos) && pos == 2);
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(simple, len - 1, &pos));
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(simple, len, &pos) && pos == 3);
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(simple, len, &pos));
+}
+
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_allcombining
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32AllCombining)
+{
+ const char32_t allcombining[] = U"\u0300\u0300\u0300";
+ const size_t len = 3;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(allcombining, len, &pos) && pos == 3);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(allcombining, len, &pos) && pos == 3);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(allcombining, len, &pos) && pos == 3);
+ pos = 3;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(allcombining, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_complex
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Complex)
+{
+ /* Combining character, "A", two combining characters, "B".*/
+ const char32_t complex[] = U"\u0300\u0041\u0300\u0320\u0042";
+ const size_t len = 5;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(complex, len, &pos) && pos == 1);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(complex, len, &pos) && pos == 4);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(complex, len, &pos) && pos == 4);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(complex, len, &pos) && pos == 4);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(complex, len, &pos) && pos == 5);
+ pos = 5;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(complex, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_invalid
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Invalid)
+{
+ /* Latin1 "À", tab, carriage return, linefeed, separated by combining characters.*/
+ const char32_t invalid[] = U"\u00C0\u0300\u0009\u0300\u000D\u0300\u000A\u0300";
+ const size_t len = 8;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 2);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 2);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 4);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 4);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 6);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 6);
+ pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 8);
+ pos = 7;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf32(invalid, len, &pos) && pos == 8);
+ pos = 8;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf32(invalid, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf32_empty
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf32Empty)
+{
+ const char32_t emtpy[] = U"";
+ const size_t len = 0;
+ int pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf32(emtpy, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf32_single
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf32Single)
+{
+ const char32_t single[] = U"0";
+ const size_t len = 1;
+ int pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(single, len, &pos) && pos == 0);
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf32(single, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf32_simple
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf32Simple)
+{
+ const char32_t simple[] = U"012";
+ const size_t len = 3;
+ int pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(simple, len, &pos) && pos == 2);
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(simple, len, &pos) && pos == 1);
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(simple, len, &pos) && pos == 0);
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf32(simple, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf32_allcombining
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf32AllCombining)
+{
+ const char32_t allcombining[] = U"\u0300\u0300\u0300";
+ const size_t len = 3;
+ int pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(allcombining, len, &pos) && pos == 0);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(allcombining, len, &pos) && pos == 0);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(allcombining, len, &pos) && pos == 0);
+ pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf32(allcombining, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf32_complex
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf32Complex)
+{
+ /* Combining character, "A", two combining characters, "B".*/
+ const char32_t complex[] = U"\u0300\u0041\u0300\u0320\u0042";
+ const size_t len = 5;
+ int pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(complex, len, &pos) && pos == 4);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(complex, len, &pos) && pos == 1);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(complex, len, &pos) && pos == 1);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(complex, len, &pos) && pos == 1);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(complex, len, &pos) && pos == 0);
+ pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf32(complex, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf32_invalid
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf32Invalid)
+{
+ /* Latin1 "À", tab, carriage return, linefeed, separated by combining characters.*/
+ const char32_t invalid[] = U"\u00C0\u0300\u0009\u0300\u000D\u0300\u000A\u0300";
+ const size_t len = 8;
+ int pos = 8;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 6);
+ pos = 7;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 6);
+ pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 4);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 4);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 2);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 2);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 0);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos) && pos == 0);
+ pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf32(invalid, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf8_empty
+ * \{ */
+TEST(string, StrCursorStepNextUtf8Empty)
+{
+ const char empty[] = "";
+ const size_t len = 0;
+ int pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(empty, len, &pos));
+ pos = 1;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(empty, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf8_single
+ * \{ */
+TEST(string, StrCursorStepNextUtf8Single)
+{
+ const char single[] = "0";
+ const size_t len = 1;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(single, len, &pos) && pos == 1);
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(single, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf8_simple
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf8Simple)
+{
+ const char simple[] = "012";
+ const size_t len = 3;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(simple, len, &pos) && pos == 1);
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(simple, len, &pos) && pos == 2);
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(simple, len - 1, &pos));
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(simple, len, &pos) && pos == 3);
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(simple, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf8_allcombining
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf8AllCombining)
+{
+ const char allcombining[] = "\xCC\x80\xCC\x80\xCC\x80";
+ const size_t len = 6;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos) && pos == 6);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos) && pos == 6);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos) && pos == 6);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos) && pos == 6);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos) && pos == 6);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos) && pos == 6);
+ pos = 6;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(allcombining, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf8_complex
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf8AllComplex)
+{
+ /* Combining character, "A", "©", two combining characters, "B".*/
+ const char complex[] = "\xCC\x80\x41\xC2\xA9\xCC\x80\xCC\xA0\x42";
+ const size_t len = 10;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 2);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 2);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 3);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 9);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 9);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 9);
+ pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 9);
+ pos = 7;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 9);
+ pos = 8;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 9);
+ pos = 9;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(complex, len, &pos) && pos == 10);
+ pos = 10;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(complex, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf8_invalid
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf8Invalid)
+{
+ /* Latin1 "À", combining, tab, carriage return, linefeed, combining.*/
+ const char invalid[] = "\xC0\xCC\x80\x09\x0D\x0A\xCC\x80";
+ const size_t len = 8;
+ int pos = 0;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 7;
+ EXPECT_TRUE(BLI_str_cursor_step_next_utf8(invalid, len, &pos) && pos == 8);
+ pos = 8;
+ EXPECT_FALSE(BLI_str_cursor_step_next_utf8(invalid, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf8_empty
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf8Empty)
+{
+ const char empty[] = "";
+ const size_t len = 0;
+ int pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(empty, len, &pos));
+ pos = 1;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(empty, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf8_single
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf8Single)
+{
+ const char single[] = "0";
+ const size_t len = 1;
+ int pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(single, len, &pos) && pos == 0);
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(single, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf8_single
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf8Simple)
+{
+ const char simple[] = "012";
+ const size_t len = 3;
+ int pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(simple, len, &pos) && pos == 2);
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(simple, len, &pos) && pos == 1);
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(simple, len, &pos) && pos == 0);
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(simple, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf8_allcombining
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf8AllCombining)
+{
+ const char allcombining[] = "\xCC\x80\xCC\x80\xCC\x80";
+ const size_t len = 6;
+ int pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos) && pos == 0);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos) && pos == 0);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos) && pos == 0);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos) && pos == 0);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos) && pos == 0);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos) && pos == 0);
+ pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(allcombining, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf8_complex
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf8Complex)
+{
+ /* Combining character, "A", "©", two combining characters, "B".*/
+ const char complex[] = "\xCC\x80\x41\xC2\xA9\xCC\x80\xCC\xA0\x42";
+ const size_t len = 10;
+ int pos = 10;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 9);
+ pos = 9;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 3);
+ pos = 8;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 3);
+ pos = 7;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 3);
+ pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 3);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 3);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 3);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 2);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 0);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(complex, len, &pos) && pos == 0);
+ pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(complex, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_prev_utf8_invalid
+ * \{ */
+
+TEST(string, StrCursorStepPrevUtf8Invalid)
+{
+ /* Latin1 "À", combining, tab, carriage return, linefeed, combining.*/
+ const char invalid[] = "\xC0\xCC\x80\x09\x0D\x0A\xCC\x80";
+ const size_t len = 8;
+ int pos = 8;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 5);
+ pos= 7;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 5);
+ pos = 6;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 5);
+ pos = 5;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 4);
+ pos = 4;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 3);
+ pos = 3;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 0);
+ pos = 2;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 0);
+ pos = 1;
+ EXPECT_TRUE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos) && pos == 0);
+ pos = 0;
+ EXPECT_FALSE(BLI_str_cursor_step_prev_utf8(invalid, len, &pos));
+}
+
+/** \} */