mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-05 19:15:33 +00:00
Support captures in replacements
This commit is contained in:
parent
59b2c88c65
commit
78c66181b1
20
README.md
20
README.md
@ -5,8 +5,9 @@ Awatcher is a window activity and idle watcher with an optional tray and UI for
|
||||
The goal is to compensate the fragmentation of desktop environments on Linux by supporting all reportable environments,
|
||||
to add more flexibility to reports with filters, and to have better UX with the distribution by a single executable.
|
||||
|
||||
The foundation is taken from [ActivityWatch](https://github.com/ActivityWatch), which includes the server and web UI.
|
||||
The unbundled watcher can replace the original idle and active window watchers in the original distribution if necessary.
|
||||
The foundation is [ActivityWatch](https://github.com/ActivityWatch), which includes the server and web UI.
|
||||
The unbundled watcher is supposed to replace the original idle and active window watchers from the original distribution.
|
||||
The bundled executable can be used independently as it contains the server, UI and tray.
|
||||
|
||||
The crate also provides a library with watchers which can send the data to the server.
|
||||
|
||||
@ -109,10 +110,21 @@ Matches are case sensitive regular expressions between implici ^ and $:
|
||||
- `.*` matches any number of any characters
|
||||
- `.+` matches 1 or more any characters.
|
||||
- `word` is an exact match.
|
||||
- Use escapes to match special characters, e.g. `org\.kde\.Dolpin`
|
||||
- Use escapes `\` to match special characters, e.g. `org\.kde\.Dolpin`
|
||||
|
||||
The replacements in filters also support regexp captures.
|
||||
A captures takes a string in parentheses from the match and replaces `$N` in the replacement, where `N` is the number of parentheses.
|
||||
Example filter to remove the changed file indicator in Visual Studio Code:
|
||||
```toml
|
||||
[[awatcher.filters]]
|
||||
match-app-id = "code"
|
||||
match-title = "● (.*)"
|
||||
# Inserts the content within 1st parentheses, this can be in any form, e.g. "App $1 - $2/$3"
|
||||
replace-title = "$1"
|
||||
```
|
||||
|
||||
Run the command with "debug" or "trace" verbosity and without reporting to server in the terminal
|
||||
to see what application names and titles are reported to the server.
|
||||
```
|
||||
$ awatcher -vvv --no-server
|
||||
```
|
||||
```
|
||||
|
@ -7,6 +7,48 @@ use crate::config::defaults;
|
||||
|
||||
use super::filters::Filter;
|
||||
|
||||
pub fn default_config() -> String {
|
||||
format!(
|
||||
r#"# The commented values are the defaults on the file creation
|
||||
[server]
|
||||
# port = {}
|
||||
# host = "{}"
|
||||
|
||||
[awatcher]
|
||||
# idle-timeout-seconds={}
|
||||
# poll-time-idle-seconds={}
|
||||
# poll-time-window-seconds={}
|
||||
|
||||
# Add as many filters as needed. The first matching filter stops the replacement.
|
||||
# There should be at least 1 match field, and at least 1 replace field.
|
||||
# Matches are case sensitive regular expressions between implici ^ and $, e.g.
|
||||
# - "." matches 1 any character
|
||||
# - ".*" matches any number of any characters
|
||||
# - ".+" matches 1 or more any characters.
|
||||
# - "word" is an exact match.
|
||||
# [[awatcher.filters]]
|
||||
# match-app-id = "navigator"
|
||||
# match-title = ".*Firefox.*"
|
||||
# replace-app-id = "firefox"
|
||||
# replace-title = "Unknown"
|
||||
|
||||
# Use captures for app-id or title in the regular form to use parts of the original text
|
||||
# (parentheses for a capture, $1, $2 etc for each capture).
|
||||
# The example rule removes the changed file indicator from the title in Visual Studio Code:
|
||||
# "● file_config.rs - awatcher - Visual Studio Code" to "file_config.rs - awatcher - Visual Studio Code".
|
||||
# [[awatcher.filters]]
|
||||
# match-app-id = "code"
|
||||
# match-title = "● (.*)"
|
||||
# replace-title = "$1"
|
||||
"#,
|
||||
defaults::port(),
|
||||
defaults::host(),
|
||||
defaults::idle_timeout_seconds(),
|
||||
defaults::poll_time_idle_seconds(),
|
||||
defaults::poll_time_window_seconds(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DefaultFromSerde)]
|
||||
pub struct ServerConfig {
|
||||
#[serde(default = "defaults::port")]
|
||||
@ -69,36 +111,7 @@ impl FileConfig {
|
||||
|
||||
toml::from_str(&config_content)?
|
||||
} else {
|
||||
let config = format!(
|
||||
r#"# The commented values are the defaults on the file creation
|
||||
[server]
|
||||
# port = {}
|
||||
# host = "{}"
|
||||
|
||||
[awatcher]
|
||||
# idle-timeout-seconds={}
|
||||
# poll-time-idle-seconds={}
|
||||
# poll-time-window-seconds={}
|
||||
|
||||
# Add as many filters as needed. The first matching filter stops the replacement.
|
||||
# There should be at least 1 match field, and at least 1 replace field.
|
||||
# Matches are case sensitive regular expressions between implici ^ and $, e.g.
|
||||
# - "." matches 1 any character
|
||||
# - ".*" matches any number of any characters
|
||||
# - ".+" matches 1 or more any characters.
|
||||
# - "word" is an exact match.
|
||||
# [[awatcher.filters]]
|
||||
# match-app-id = "navigator"
|
||||
# match-title = ".*Firefox.*"
|
||||
# replace-app-id = "firefox"
|
||||
# replace-title = "Unknown"
|
||||
"#,
|
||||
defaults::port(),
|
||||
defaults::host(),
|
||||
defaults::idle_timeout_seconds(),
|
||||
defaults::poll_time_idle_seconds(),
|
||||
defaults::poll_time_window_seconds(),
|
||||
);
|
||||
let config = default_config();
|
||||
let error = std::fs::create_dir(config_path.parent().unwrap());
|
||||
if let Err(e) = error {
|
||||
if e.kind() != ErrorKind::AlreadyExists {
|
||||
|
@ -39,8 +39,10 @@ pub struct Replacement {
|
||||
|
||||
impl Filter {
|
||||
fn is_valid(&self) -> bool {
|
||||
(self.match_app_id.is_some() || self.match_title.is_some())
|
||||
&& (self.replace_app_id.is_some() || self.replace_title.is_some())
|
||||
let is_match_set = self.match_app_id.is_some() || self.match_title.is_some();
|
||||
let is_replacement_set = self.replace_app_id.is_some() || self.replace_title.is_some();
|
||||
|
||||
is_match_set && is_replacement_set
|
||||
}
|
||||
|
||||
fn is_match(&self, app_id: &str, title: &str) -> bool {
|
||||
@ -58,22 +60,29 @@ impl Filter {
|
||||
true
|
||||
}
|
||||
|
||||
fn replace(regex: &Option<Regex>, source: &str, replacement: &str) -> String {
|
||||
if let Some(regex) = regex {
|
||||
// Avoid using the more expensive regexp replacements when unnecessary.
|
||||
if regex.captures_len() > 1 {
|
||||
return regex.replace(source, replacement).to_string();
|
||||
}
|
||||
}
|
||||
replacement.to_owned()
|
||||
}
|
||||
|
||||
pub fn replacement(&self, app_id: &str, title: &str) -> Option<Replacement> {
|
||||
if !self.is_valid() {
|
||||
if !self.is_valid() || !self.is_match(app_id, title) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.is_match(app_id, title) {
|
||||
let mut replacement = Replacement::default();
|
||||
if let Some(new_app_id) = &self.replace_app_id {
|
||||
replacement.replace_app_id = Some(new_app_id.to_string());
|
||||
}
|
||||
if let Some(new_title) = &self.replace_title {
|
||||
replacement.replace_title = Some(new_title.to_string());
|
||||
}
|
||||
Some(replacement)
|
||||
} else {
|
||||
None
|
||||
let mut replacement = Replacement::default();
|
||||
if let Some(new_app_id) = &self.replace_app_id {
|
||||
replacement.replace_app_id =
|
||||
Some(Self::replace(&self.match_app_id, app_id, new_app_id));
|
||||
}
|
||||
if let Some(new_title) = &self.replace_title {
|
||||
replacement.replace_title = Some(Self::replace(&self.match_title, title, new_title));
|
||||
}
|
||||
Some(replacement)
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,11 @@ impl ReportClient {
|
||||
} else {
|
||||
title.to_string()
|
||||
};
|
||||
trace!("Reporting app_id: {}, title: {}", app_id, title);
|
||||
trace!(
|
||||
"Reporting app_id: {}, title: {}",
|
||||
inserted_app_id,
|
||||
inserted_title
|
||||
);
|
||||
data.insert("app".to_string(), Value::String(inserted_app_id));
|
||||
data.insert("title".to_string(), Value::String(inserted_title));
|
||||
let event = AwEvent {
|
||||
|
Loading…
x
Reference in New Issue
Block a user