diff options
| author | Maxime Coste <frrrwww@gmail.com> | 2015-03-30 23:05:24 +0100 |
|---|---|---|
| committer | Maxime Coste <frrrwww@gmail.com> | 2015-03-30 23:05:24 +0100 |
| commit | 13a5af70aebd85f5c21cba346f22eadd98fe333c (patch) | |
| tree | 3f628b894fe7dbb76026e219def4294cfc3cc44a /src | |
| parent | 8761fc34f4dedee89f8cd61be8d08845d09f211e (diff) | |
Add a format function for printf like formatting
Diffstat (limited to 'src')
| -rw-r--r-- | src/string.cc | 42 | ||||
| -rw-r--r-- | src/string.hh | 22 | ||||
| -rw-r--r-- | src/unit_tests.cc | 2 |
3 files changed, 66 insertions, 0 deletions
diff --git a/src/string.cc b/src/string.cc index df7bfa68..ae454264 100644 --- a/src/string.cc +++ b/src/string.cc @@ -205,4 +205,46 @@ Vector<StringView> wrap_lines(StringView text, CharCount max_width) return lines; } +String format(StringView fmt, ArrayView<const StringView> params) +{ + ByteCount size = fmt.length(); + for (auto& s : params) size += s.length(); + String res; + res.reserve(size); + + int implicitIndex = 0; + for (auto it = fmt.begin(), end = fmt.end(); it != end;) + { + auto opening = std::find(it, end, '{'); + res += StringView{it, opening}; + if (opening == end) + break; + + if (opening != it && res.back() == '\\') + { + res.back() = '{'; + it = opening + 1; + } + else + { + auto closing = std::find(it, end, '}'); + if (closing == end) + throw runtime_error("Format string error, unclosed '{'"); + int index; + if (closing == opening + 1) + index = implicitIndex; + else + index = str_to_int({opening+1, closing}); + + if (index >= params.size()) + throw runtime_error("Format string parameter index too big"); + + res += params[index]; + implicitIndex = index+1; + it = closing+1; + } + } + return res; +} + } diff --git a/src/string.hh b/src/string.hh index 5e90e98d..41dbccb8 100644 --- a/src/string.hh +++ b/src/string.hh @@ -5,6 +5,7 @@ #include "utf8.hh" #include "hash.hh" #include "vector.hh" +#include "array_view.hh" #include <string> #include <climits> @@ -276,6 +277,27 @@ String expand_tabs(StringView line, CharCount tabstop, CharCount col = 0); Vector<StringView> wrap_lines(StringView text, CharCount max_width); +namespace detail +{ + +template<typename T> using IsString = std::is_convertible<T, StringView>; + +template<typename T, class = typename std::enable_if<!IsString<T>::value>::type> +String format_param(const T& val) { return to_string(val); } + +template<typename T, class = typename std::enable_if<IsString<T>::value>::type> +StringView format_param(const T& val) { return val; } + +} + +String format(StringView fmt, ArrayView<const StringView> params); + +template<typename... Types> +String format(StringView fmt, Types... params) +{ + return format(fmt, ArrayView<const StringView>{{detail::format_param(params)...}}); +} + } #endif // string_hh_INCLUDED diff --git a/src/unit_tests.cc b/src/unit_tests.cc index ec9bb64c..9fb1bae2 100644 --- a/src/unit_tests.cc +++ b/src/unit_tests.cc @@ -137,6 +137,8 @@ void test_string() kak_assert(subsequence_match("tchou kanaky", "knk")); kak_assert(subsequence_match("tchou kanaky", "tchou kanaky")); kak_assert(not subsequence_match("tchou kanaky", "tchou kanaky")); + + kak_assert(format("Youhou {1} {} {0} \\{}", 10, "hehe", 5) == "Youhou hehe 5 10 {}"); } void test_keys() |
