refactor: separate regex rule matching and caching in separate class

This commit is contained in:
Brenno Lemos 2023-10-09 13:53:00 -03:00
parent 30cc88a4c5
commit 8d057e6f96
6 changed files with 149 additions and 62 deletions

View File

@ -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_;

View File

@ -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

View File

@ -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']

View File

@ -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

View File

@ -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

View File

@ -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