summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2015-03-30 23:05:24 +0100
committerMaxime Coste <frrrwww@gmail.com>2015-03-30 23:05:24 +0100
commit13a5af70aebd85f5c21cba346f22eadd98fe333c (patch)
tree3f628b894fe7dbb76026e219def4294cfc3cc44a /src
parent8761fc34f4dedee89f8cd61be8d08845d09f211e (diff)
Add a format function for printf like formatting
Diffstat (limited to 'src')
-rw-r--r--src/string.cc42
-rw-r--r--src/string.hh22
-rw-r--r--src/unit_tests.cc2
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()