refactor: separate regex rule matching and caching in separate class
This commit is contained in:
		
							parent
							
								
									30cc88a4c5
								
							
						
					
					
						commit
						8d057e6f96
					
				|  | @ -16,8 +16,10 @@ | |||
| #include "bar.hpp" | ||||
| #include "modules/hyprland/backend.hpp" | ||||
| #include "util/enum.hpp" | ||||
| #include "util/regex_collection.hpp" | ||||
| 
 | ||||
| using WindowAddress = std::string; | ||||
| 
 | ||||
| namespace waybar::modules::hyprland { | ||||
| 
 | ||||
| class Workspaces; | ||||
|  | @ -47,7 +49,7 @@ class Workspace { | |||
|   void set_windows(uint value) { windows_ = value; }; | ||||
|   void set_name(std::string value) { name_ = value; }; | ||||
|   bool contains_window(WindowAddress addr) { return window_map_.contains(addr); } | ||||
|   void insert_window(WindowAddress addr, std::string window_repr); | ||||
|   void insert_window(WindowAddress addr, std::string window_class, std::string window_title); | ||||
|   std::string remove_window(WindowAddress addr); | ||||
|   void initialize_window_map(const Json::Value& clients_data); | ||||
| 
 | ||||
|  | @ -92,7 +94,7 @@ class Workspaces : public AModule, public EventHandler { | |||
| 
 | ||||
|   auto get_bar_output() const -> std::string { return bar_.output->name; } | ||||
| 
 | ||||
|   std::string get_rewrite(std::string window_class); | ||||
|   std::string get_rewrite(std::string window_class, std::string window_title); | ||||
|   std::string& get_window_separator() { return format_window_separator_; } | ||||
| 
 | ||||
|  private: | ||||
|  | @ -129,11 +131,12 @@ class Workspaces : public AModule, public EventHandler { | |||
|   bool persistent_created_ = false; | ||||
| 
 | ||||
|   std::string format_; | ||||
| 
 | ||||
|   std::map<std::string, std::string> icons_map_; | ||||
|   Json::Value window_rewrite_rules_; | ||||
|   std::map<std::string, std::string> regex_cache_; | ||||
|   util::RegexCollection window_rewrite_rules_; | ||||
|   bool any_window_rewrite_rule_uses_title_ = false; | ||||
|   std::string format_window_separator_; | ||||
|   std::string window_rewrite_default_; | ||||
| 
 | ||||
|   bool with_icon_; | ||||
|   uint64_t monitor_id_; | ||||
|   std::string active_workspace_name_; | ||||
|  |  | |||
|  | @ -0,0 +1,37 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <json/json.h> | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <regex> | ||||
| #include <string> | ||||
| 
 | ||||
| namespace waybar::util { | ||||
| 
 | ||||
| struct Rule { | ||||
|   std::regex rule; | ||||
|   std::string repr; | ||||
|   int priority; | ||||
| }; | ||||
| 
 | ||||
| int default_priority_function(std::string& key); | ||||
| 
 | ||||
| class RegexCollection { | ||||
|  private: | ||||
|   std::vector<Rule> rules; | ||||
|   std::map<std::string, std::string> regex_cache; | ||||
|   std::string default_repr; | ||||
| 
 | ||||
|   std::string& find_match(std::string& value, bool& matched_any); | ||||
| 
 | ||||
|  public: | ||||
|   RegexCollection() = default; | ||||
|   RegexCollection(const Json::Value& map, std::string default_repr = "", | ||||
|                   std::function<int(std::string&)> priority_function = default_priority_function); | ||||
|   ~RegexCollection() = default; | ||||
| 
 | ||||
|   std::string& get(std::string& value, bool& matched_any); | ||||
|   std::string& get(std::string& value); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace waybar::util
 | ||||
|  | @ -177,7 +177,8 @@ src_files = files( | |||
|     'src/util/ustring_clen.cpp', | ||||
|     'src/util/sanitize_str.cpp', | ||||
|     'src/util/rewrite_string.cpp', | ||||
|     'src/util/gtk_icon.cpp' | ||||
|     'src/util/gtk_icon.cpp', | ||||
|     'src/util/regex_collection.cpp' | ||||
| ) | ||||
| 
 | ||||
| inc_dirs = ['include'] | ||||
|  |  | |||
|  | @ -1,19 +1,34 @@ | |||
| #include "modules/hyprland/workspaces.hpp" | ||||
| 
 | ||||
| #include <fmt/ostream.h> | ||||
| #include <json/value.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <charconv> | ||||
| #include <memory> | ||||
| #include <optional> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "util/rewrite_string.hpp" | ||||
| #include "util/regex_collection.hpp" | ||||
| 
 | ||||
| namespace waybar::modules::hyprland { | ||||
| 
 | ||||
| int window_rewrite_priority_function(std::string &window_rule) { | ||||
|   // Rules that match against title are prioritized
 | ||||
|   // Rules that don't specify if they're matching against either title or class are deprioritized
 | ||||
|   bool has_title = window_rule.find("title") != std::string::npos; | ||||
|   bool has_class = window_rule.find("class") != std::string::npos; | ||||
| 
 | ||||
|   if (has_title && has_class) { | ||||
|     return 3; | ||||
|   } else if (has_title) { | ||||
|     return 2; | ||||
|   } else if (has_class) { | ||||
|     return 1; | ||||
|   } else { | ||||
|     return 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) | ||||
|     : AModule(config, "workspaces", id, false, false), | ||||
|       bar_(bar), | ||||
|  | @ -77,11 +92,14 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { | |||
|   format_window_separator_ = | ||||
|       format_window_separator.isString() ? format_window_separator.asString() : " "; | ||||
| 
 | ||||
|   window_rewrite_rules_ = config["window-rewrite"]; | ||||
|   Json::Value window_rewrite = config["window-rewrite"]; | ||||
| 
 | ||||
|   Json::Value window_rewrite_default = config["window-rewrite-default"]; | ||||
|   window_rewrite_default_ = | ||||
|       window_rewrite_default.isString() ? window_rewrite_default.asString() : "?"; | ||||
|   Json::Value window_rewrite_default_config = config["window-rewrite-default"]; | ||||
|   std::string window_rewrite_default = | ||||
|       window_rewrite_default_config.isString() ? window_rewrite_default_config.asString() : "?"; | ||||
| 
 | ||||
|   window_rewrite_rules_ = util::RegexCollection(window_rewrite, window_rewrite_default, | ||||
|                                                 window_rewrite_priority_function); | ||||
| } | ||||
| 
 | ||||
| auto Workspaces::register_ipc() -> void { | ||||
|  | @ -323,13 +341,14 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { | |||
|       // {ADDR}
 | ||||
|       WindowAddress client_address = client["address"].asString(); | ||||
|       client_address = client_address.substr(2, client_address.length() - 2); | ||||
|       insert_window(client_address, client["class"].asString()); | ||||
|       insert_window(client_address, client["class"].asString(), client["title"].asString()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void Workspace::insert_window(WindowAddress addr, std::string window_class) { | ||||
|   auto window_repr = workspace_manager_.get_rewrite(window_class); | ||||
| void Workspace::insert_window(WindowAddress addr, std::string window_class, | ||||
|                               std::string window_title) { | ||||
|   auto window_repr = workspace_manager_.get_rewrite(window_class, window_title); | ||||
|   if (!window_repr.empty()) { | ||||
|     window_map_.emplace(addr, window_repr); | ||||
|   } | ||||
|  | @ -355,7 +374,7 @@ bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_nam | |||
| bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, | ||||
|                                  std::string &window_class, std::string &window_title) { | ||||
|   if (workspace_name == name()) { | ||||
|     insert_window(addr, window_class); | ||||
|     insert_window(addr, window_class, window_title); | ||||
|     return true; | ||||
|   } else { | ||||
|     return false; | ||||
|  | @ -766,23 +785,9 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| std::string Workspaces::get_rewrite(std::string window_class) { | ||||
|   if (regex_cache_.contains(window_class)) { | ||||
|     return regex_cache_[window_class]; | ||||
|   } | ||||
| 
 | ||||
|   bool matched_any; | ||||
| 
 | ||||
|   std::string window_class_rewrite = | ||||
|       waybar::util::rewriteStringOnce(window_class, window_rewrite_rules_, matched_any); | ||||
| 
 | ||||
|   if (!matched_any) { | ||||
|     window_class_rewrite = window_rewrite_default_; | ||||
|   } | ||||
| 
 | ||||
|   regex_cache_.emplace(window_class, window_class_rewrite); | ||||
| 
 | ||||
|   return window_class_rewrite; | ||||
| std::string Workspaces::get_rewrite(std::string window_class, std::string window_title) { | ||||
|   std::string window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title); | ||||
|   return window_rewrite_rules_.get(window_repr_key); | ||||
| } | ||||
| 
 | ||||
| }  // namespace waybar::modules::hyprland
 | ||||
|  |  | |||
|  | @ -0,0 +1,69 @@ | |||
| #include "util/regex_collection.hpp" | ||||
| 
 | ||||
| #include <json/value.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| 
 | ||||
| namespace waybar::util { | ||||
| 
 | ||||
| int default_priority_function(std::string& key) { return 0; } | ||||
| 
 | ||||
| RegexCollection::RegexCollection(const Json::Value& map, std::string default_repr, | ||||
|                                  std::function<int(std::string&)> priority_function) | ||||
|     : default_repr(default_repr) { | ||||
|   if (!map.isObject()) { | ||||
|     spdlog::warn("Mapping is not an object"); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   for (auto it = map.begin(); it != map.end(); ++it) { | ||||
|     if (it.key().isString() && it->isString()) { | ||||
|       std::string key = it.key().asString(); | ||||
|       int priority = priority_function(key); | ||||
|       try { | ||||
|         const std::regex rule{key, std::regex_constants::icase}; | ||||
|         rules.emplace_back(rule, it->asString(), priority); | ||||
|       } catch (const std::regex_error& e) { | ||||
|         spdlog::error("Invalid rule '{}': {}", key, e.what()); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   std::sort(rules.begin(), rules.end(), [](Rule& a, Rule& b) { return a.priority > b.priority; }); | ||||
| } | ||||
| 
 | ||||
| std::string& RegexCollection::find_match(std::string& value, bool& matched_any) { | ||||
|   for (auto& rule : rules) { | ||||
|     if (std::regex_search(value, rule.rule)) { | ||||
|       matched_any = true; | ||||
|       return rule.repr; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return value; | ||||
| } | ||||
| 
 | ||||
| std::string& RegexCollection::get(std::string& value, bool& matched_any) { | ||||
|   if (regex_cache.contains(value)) { | ||||
|     return regex_cache[value]; | ||||
|   } | ||||
| 
 | ||||
|   // std::string repr =
 | ||||
|   // waybar::util::find_match(value, window_rewrite_rules_, matched_any);
 | ||||
| 
 | ||||
|   std::string repr = find_match(value, matched_any); | ||||
| 
 | ||||
|   if (!matched_any) { | ||||
|     repr = default_repr; | ||||
|   } | ||||
| 
 | ||||
|   regex_cache.emplace(value, repr); | ||||
| 
 | ||||
|   return regex_cache[value];  // Necessary in order to return a reference to the heap
 | ||||
| } | ||||
| 
 | ||||
| std::string& RegexCollection::get(std::string& value) { | ||||
|   bool matched_any = false; | ||||
|   return get(value, matched_any); | ||||
| } | ||||
| 
 | ||||
| }  // namespace waybar::util
 | ||||
|  | @ -1,6 +1,5 @@ | |||
| #include "util/rewrite_string.hpp" | ||||
| 
 | ||||
| #include <fmt/core.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| 
 | ||||
| #include <regex> | ||||
|  | @ -30,31 +29,4 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) { | |||
| 
 | ||||
|   return res; | ||||
| } | ||||
| 
 | ||||
| std::string rewriteStringOnce(const std::string& value, const Json::Value& rules, | ||||
|                               bool& matched_any) { | ||||
|   if (!rules.isObject()) { | ||||
|     return value; | ||||
|   } | ||||
| 
 | ||||
|   matched_any = false; | ||||
| 
 | ||||
|   std::string res = value; | ||||
| 
 | ||||
|   for (auto it = rules.begin(); it != rules.end(); ++it) { | ||||
|     if (it.key().isString() && it->isString()) { | ||||
|       try { | ||||
|         const std::regex rule{it.key().asString(), std::regex_constants::icase}; | ||||
|         if (std::regex_match(value, rule)) { | ||||
|           matched_any = true; | ||||
|           return std::regex_replace(res, rule, it->asString()); | ||||
|         } | ||||
|       } catch (const std::regex_error& e) { | ||||
|         spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what()); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return value; | ||||
| } | ||||
| }  // namespace waybar::util
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue