@@ -36,6 +36,7 @@ extern "C" {
3636#include < cstdlib> // KILLME
3737#include < string>
3838#include < optional>
39+ #include < regex>
3940#include < utility>
4041#include < filesystem>
4142#include < cairo.h>
@@ -341,7 +342,7 @@ class Fact {
341342 Fact (FactMeta meta, double val): meta(meta), value(val), type(T_DOUBLE) {};
342343 Fact (FactMeta meta, std::string val): meta(meta), value(val), type(T_STRING) {};
343344
344- bool isDefined () {
345+ bool isDefined () const {
345346 return type != T_UNDEF;
346347 }
347348
@@ -827,7 +828,9 @@ class IconTextWidget: public Widget {
827828class TplTextWidget : public Widget {
828829public:
829830 TplTextWidget (int pos_x, int pos_y, std::string tpl, uint num_args):
830- Widget (pos_x, pos_y, num_args), tpl(tpl), num_args(num_args) {};
831+ Widget (pos_x, pos_y, num_args), tpl(tpl), num_args(num_args) {
832+ _tokens = tokenize (tpl);
833+ };
831834
832835 virtual void draw (cairo_t *cr) {
833836 auto [x, y] = xy (cr);
@@ -840,77 +843,113 @@ class TplTextWidget: public Widget {
840843 uint default_precision = 2 ;
841844
842845protected:
846+ enum class TokenType {
847+ Literal,
848+ Bool,
849+ Int,
850+ Uint,
851+ Float,
852+ String
853+ };
854+
855+ struct Token {
856+ TokenType type;
857+ std::optional<std::string> value; // Used to hold literal
858+ uint precision; // Precision for float placeholders if applicable
859+
860+ Token (TokenType t, std::string v) // literal
861+ : type(t), value(std::move(v)), precision(0 ) {}
862+ Token (TokenType t, uint p) // float
863+ : type(t), value(std::nullopt ), precision(p) {}
864+ Token (TokenType t) // other
865+ : type(t), value(std::nullopt ), precision(0 ) {}
866+ };
867+
843868 std::unique_ptr<std::string> render_tpl () {
844- return render_tpl (tpl, args);
869+ return render_tokens (_tokens, args);
870+ }
871+
872+ std::unique_ptr<std::string> render_tpl (const std::string& tpl, const std::vector<Fact>& facts) {
873+ auto tokens = tokenize (tpl);
874+ return render_tokens (tokens, facts);
845875 }
846- std::unique_ptr<std::string> render_tpl (std::string tpl, const std::vector<Fact>& args) {
847- bool at_placeholder = false ;
848- bool at_precision = false ;
849- uint precision = default_precision;
850- int fact_i = 0 ;
876+
877+ std::unique_ptr<std::string> render_tokens (const std::vector<Token>& tokens,
878+ const std::vector<Fact>& facts) {
851879 std::ostringstream msg;
852- for (char & c : tpl) {
853- if (c == ' %' ) {
854- at_placeholder = true ;
855- } else if (!at_placeholder) {
856- msg << c;
857- } else if (at_placeholder && c == ' %' ) {
858- msg << ' %' ;
859- at_placeholder = false ;
860- } else if (at_placeholder) {
861- if (at_precision && std::isdigit (c)) {
862- precision = precision * 10 + (c - ' 0' );
863- continue ; // exit early
864- }
865- at_precision = false ;
866- if (fact_i >= args.size ()) {
867- msg << ' ?' ; // Handle out-of-bounds fact access
868- break ;
869- }
870- const Fact& fact = args.at (fact_i);
871- if (!fact.isDefined ()) {
872- msg << ' ?' ;
873- fact_i++;
874- continue ;
875- }
876- switch (c) {
877- case ' b' :
878- msg << (fact.getBoolValue () ? ' t' : ' f' );
879- fact_i++;
880- break ;
881- case ' d' :
882- case ' i' :
883- msg << fact.getIntValue ();
884- fact_i++;
885- break ;
886- case ' u' :
887- msg << fact.getUintValue ();
888- fact_i++;
889- break ;
890- case ' f' :
891- msg << std::fixed << std::setprecision (precision) << fact.getDoubleValue ();
892- fact_i++;
893- break ;
894- case ' s' :
895- msg << fact.getStrValue ();
896- fact_i++;
897- break ;
898- case ' .' :
899- // beginning of float precision specifier `%.3f` / `%.123f` / `%.0f`
900- at_precision = true ;
901- precision = 0 ;
902- continue ; // exit earlier to not reset `at_placeholder`
903- default :
904- msg << ' ?' ;
880+ size_t fact_i = 0 ; // To track the current index in the facts vector
881+
882+ for (const Token& token : tokens) {
883+ if (token.type == TokenType::Literal) {
884+ msg << *token.value ; // Append literal directly, dereference std::optional
885+ } else {
886+ // Check if we have enough facts and if the current fact is defined
887+ if (fact_i >= facts.size () || !facts[fact_i].isDefined ()) {
888+ msg << ' ?' ; // Append '?' for undefined facts
889+ } else {
890+ switch (token.type ) {
891+ case TokenType::Bool:
892+ msg << (facts[fact_i].getBoolValue () ? ' t' : ' f' );
893+ break ;
894+ case TokenType::Int:
895+ msg << facts[fact_i].getIntValue ();
896+ break ;
897+ case TokenType::Uint:
898+ msg << facts[fact_i].getUintValue ();
899+ break ;
900+ case TokenType::Float:
901+ msg << std::fixed << std::setprecision (token.precision ) << facts[fact_i].getDoubleValue ();
902+ break ;
903+ case TokenType::String:
904+ msg << facts[fact_i].getStrValue ();
905+ break ;
906+ }
905907 }
906- at_placeholder = false ;
908+ fact_i++; // Move to the next fact for the next placeholder
907909 }
908910 }
909911 return std::make_unique<std::string>(msg.str ());
910912 }
911913
912- protected:
914+ std::vector<Token> tokenize (const std::string& tpl) {
915+ std::vector<Token> tokens;
916+ std::regex token_regex (R"( %%|%[bisu]|%(\.\d+)?f|[^%]+)" ); // Match placeholders and literals
917+ std::sregex_iterator iter (tpl.begin (), tpl.end (), token_regex);
918+ std::sregex_iterator end;
919+
920+ while (iter != end) {
921+ std::string match = iter->str ();
922+ if (match == " %%" ) {
923+ tokens.emplace_back (TokenType::Literal, " %" );
924+ } else if (match[0 ] == ' %' ) {
925+ if (match.size () == 2 ) { // Simple placeholder like %b, %i, %u, %s
926+ if (match[1 ] == ' b' ) {
927+ tokens.emplace_back (TokenType::Bool);
928+ } else if (match[1 ] == ' i' || match[1 ] == ' d' ) {
929+ tokens.emplace_back (TokenType::Int);
930+ } else if (match[1 ] == ' u' ) {
931+ tokens.emplace_back (TokenType::Uint);
932+ } else if (match[1 ] == ' s' ) {
933+ tokens.emplace_back (TokenType::String);
934+ }
935+ } else if (match.back () == ' f' ) { // Float placeholder
936+ uint precision = 0 ;
937+ if (match.size () > 2 && match[1 ] == ' .' ) {
938+ precision = std::stoi (match.substr (2 , match.size () - 3 )); // Extract precision
939+ }
940+ tokens.emplace_back (TokenType::Float, precision); // Add float token
941+ }
942+ } else {
943+ tokens.emplace_back (TokenType::Literal, match); // Accumulate literal
944+ }
945+ ++iter;
946+ }
947+
948+ return tokens;
949+ }
950+
913951 std::string tpl;
952+ std::vector<Token> _tokens;
914953 uint num_args;
915954};
916955
0 commit comments