diff --git a/src/common-tests/CMakeLists.txt b/src/common-tests/CMakeLists.txt
index 86bec30d6..3aadc3002 100644
--- a/src/common-tests/CMakeLists.txt
+++ b/src/common-tests/CMakeLists.txt
@@ -3,6 +3,7 @@ add_executable(common-tests
file_system_tests.cpp
path_tests.cpp
rectangle_tests.cpp
+ string_tests.cpp
)
target_link_libraries(common-tests PRIVATE common gtest gtest_main)
diff --git a/src/common-tests/common-tests.vcxproj b/src/common-tests/common-tests.vcxproj
index 25bc2bccb..7df80bf06 100644
--- a/src/common-tests/common-tests.vcxproj
+++ b/src/common-tests/common-tests.vcxproj
@@ -7,6 +7,7 @@
+
diff --git a/src/common-tests/common-tests.vcxproj.filters b/src/common-tests/common-tests.vcxproj.filters
index d9efc532d..b7b2e270d 100644
--- a/src/common-tests/common-tests.vcxproj.filters
+++ b/src/common-tests/common-tests.vcxproj.filters
@@ -6,5 +6,6 @@
+
\ No newline at end of file
diff --git a/src/common-tests/string_tests.cpp b/src/common-tests/string_tests.cpp
new file mode 100644
index 000000000..518d21c33
--- /dev/null
+++ b/src/common-tests/string_tests.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin
+// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
+
+#include "common/string_util.h"
+#include
+
+TEST(StringUtil, Ellipsise)
+{
+ ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 6, "..."), "Hel...");
+ ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 7, ".."), "Hello..");
+ ASSERT_EQ(StringUtil::Ellipsise("HelloWorld", 20, ".."), "HelloWorld");
+ ASSERT_EQ(StringUtil::Ellipsise("", 20, "..."), "");
+ ASSERT_EQ(StringUtil::Ellipsise("Hello", 10, "..."), "Hello");
+}
+
+TEST(StringUtil, EllipsiseInPlace)
+{
+ std::string s;
+ s = "HelloWorld";
+ StringUtil::EllipsiseInPlace(s, 6, "...");
+ ASSERT_EQ(s, "Hel...");
+ s = "HelloWorld";
+ StringUtil::EllipsiseInPlace(s, 7, "..");
+ ASSERT_EQ(s, "Hello..");
+ s = "HelloWorld";
+ StringUtil::EllipsiseInPlace(s, 20, "..");
+ ASSERT_EQ(s, "HelloWorld");
+ s = "";
+ StringUtil::EllipsiseInPlace(s, 20, "...");
+ ASSERT_EQ(s, "");
+ s = "Hello";
+ StringUtil::EllipsiseInPlace(s, 10, "...");
+ ASSERT_EQ(s, "Hello");
+}
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 25964092a..140d94606 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -1,7 +1,9 @@
-// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin
+// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
+#include "assert.h"
#include "string_util.h"
+
#include
#include
#include
@@ -395,6 +397,47 @@ invalid:
return 1;
}
+std::string StringUtil::Ellipsise(const std::string_view& str, u32 max_length, const char* ellipsis /*= "..."*/)
+{
+ std::string ret;
+ ret.reserve(max_length);
+
+ const u32 str_length = static_cast(str.length());
+ const u32 ellipsis_len = static_cast(std::strlen(ellipsis));
+ DebugAssert(ellipsis_len > 0 && ellipsis_len <= max_length);
+
+ if (str_length > max_length)
+ {
+ const u32 copy_size = std::min(str_length, max_length - ellipsis_len);
+ if (copy_size > 0)
+ ret.append(str.data(), copy_size);
+ if (copy_size != str_length)
+ ret.append(ellipsis);
+ }
+ else
+ {
+ ret.append(str);
+ }
+
+ return ret;
+}
+
+void StringUtil::EllipsiseInPlace(std::string& str, u32 max_length, const char* ellipsis /*= "..."*/)
+{
+ const u32 str_length = static_cast(str.length());
+ const u32 ellipsis_len = static_cast(std::strlen(ellipsis));
+ DebugAssert(ellipsis_len > 0 && ellipsis_len <= max_length);
+
+ if (str_length > max_length)
+ {
+ const u32 keep_size = std::min(static_cast(str.length()), max_length - ellipsis_len);
+ if (keep_size != str_length)
+ str.erase(keep_size);
+
+ str.append(ellipsis);
+ }
+}
+
size_t StringUtil::DecodeUTF8(const std::string_view& str, size_t offset, char32_t* ch)
{
return DecodeUTF8(str.data() + offset, str.length() - offset, ch);
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 78432a83b..c14822eb6 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -271,6 +271,10 @@ size_t DecodeUTF8(const void* bytes, size_t length, char32_t* ch);
size_t DecodeUTF8(const std::string_view& str, size_t offset, char32_t* ch);
size_t DecodeUTF8(const std::string& str, size_t offset, char32_t* ch);
+// Replaces the end of a string with ellipsis if it exceeds the specified length.
+std::string Ellipsise(const std::string_view& str, u32 max_length, const char* ellipsis = "...");
+void EllipsiseInPlace(std::string& str, u32 max_length, const char* ellipsis = "...");
+
/// Strided memcpy/memcmp.
ALWAYS_INLINE static void StrideMemCpy(void* dst, std::size_t dst_stride, const void* src, std::size_t src_stride,
std::size_t copy_size, std::size_t count)