First steps in passive match rendering
This commit is contained in:
parent
6eec895b21
commit
0eb58704a9
|
@ -54,6 +54,8 @@ fn default_config_caching_interval() -> i32 { 800 }
|
||||||
fn default_word_separators() -> Vec<char> { vec![' ', ',', '.', '\r', '\n', 22u8 as char] }
|
fn default_word_separators() -> Vec<char> { vec![' ', ',', '.', '\r', '\n', 22u8 as char] }
|
||||||
fn default_toggle_interval() -> u32 { 230 }
|
fn default_toggle_interval() -> u32 { 230 }
|
||||||
fn default_preserve_clipboard() -> bool {false}
|
fn default_preserve_clipboard() -> bool {false}
|
||||||
|
fn default_passive_match_regex() -> String{ "(?P<name>:\\p{L}+)(/(?P<args>.*)/)?".to_owned() }
|
||||||
|
fn default_passive_arg_regex() -> String{ "((\\\\/)|[^/])+".to_owned() }
|
||||||
fn default_backspace_limit() -> i32 { 3 }
|
fn default_backspace_limit() -> i32 { 3 }
|
||||||
fn default_exclude_default_matches() -> bool {false}
|
fn default_exclude_default_matches() -> bool {false}
|
||||||
fn default_matches() -> Vec<Match> { Vec::new() }
|
fn default_matches() -> Vec<Match> { Vec::new() }
|
||||||
|
@ -102,6 +104,12 @@ pub struct Configs {
|
||||||
#[serde(default = "default_preserve_clipboard")]
|
#[serde(default = "default_preserve_clipboard")]
|
||||||
pub preserve_clipboard: bool,
|
pub preserve_clipboard: bool,
|
||||||
|
|
||||||
|
#[serde(default = "default_passive_match_regex")]
|
||||||
|
pub passive_match_regex: String,
|
||||||
|
|
||||||
|
#[serde(default = "default_passive_arg_regex")]
|
||||||
|
pub passive_arg_regex: String,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub paste_shortcut: PasteShortcut,
|
pub paste_shortcut: PasteShortcut,
|
||||||
|
|
||||||
|
@ -150,6 +158,8 @@ impl Configs {
|
||||||
validate_field!(result, self.ipc_server_port, default_ipc_server_port());
|
validate_field!(result, self.ipc_server_port, default_ipc_server_port());
|
||||||
validate_field!(result, self.use_system_agent, default_use_system_agent());
|
validate_field!(result, self.use_system_agent, default_use_system_agent());
|
||||||
validate_field!(result, self.preserve_clipboard, default_preserve_clipboard());
|
validate_field!(result, self.preserve_clipboard, default_preserve_clipboard());
|
||||||
|
validate_field!(result, self.passive_match_regex, default_passive_match_regex());
|
||||||
|
validate_field!(result, self.passive_arg_regex, default_passive_arg_regex());
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ use fs2::FileExt;
|
||||||
use log::{info, warn, LevelFilter};
|
use log::{info, warn, LevelFilter};
|
||||||
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger, WriteLogger};
|
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger, WriteLogger};
|
||||||
|
|
||||||
use crate::config::ConfigSet;
|
use crate::config::{ConfigSet, ConfigManager};
|
||||||
use crate::config::runtime::RuntimeConfigManager;
|
use crate::config::runtime::RuntimeConfigManager;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::event::*;
|
use crate::event::*;
|
||||||
|
@ -333,7 +333,8 @@ fn daemon_background(receive_channel: Receiver<Event>, config_set: ConfigSet) {
|
||||||
|
|
||||||
let extensions = extension::get_extensions();
|
let extensions = extension::get_extensions();
|
||||||
|
|
||||||
let renderer = render::default::DefaultRenderer::new(extensions);
|
let renderer = render::default::DefaultRenderer::new(extensions,
|
||||||
|
config_manager.default_config().clone());
|
||||||
|
|
||||||
let engine = Engine::new(&keyboard_manager,
|
let engine = Engine::new(&keyboard_manager,
|
||||||
&clipboard_manager,
|
&clipboard_manager,
|
||||||
|
|
|
@ -33,18 +33,36 @@ lazy_static! {
|
||||||
|
|
||||||
pub struct DefaultRenderer {
|
pub struct DefaultRenderer {
|
||||||
extension_map: HashMap<String, Box<dyn Extension>>,
|
extension_map: HashMap<String, Box<dyn Extension>>,
|
||||||
|
|
||||||
|
// Regex used to identify matches (and arguments) in passive expansions
|
||||||
|
passive_match_regex: Regex,
|
||||||
|
|
||||||
|
// Regex used to separate arguments in passive expansions
|
||||||
|
passive_arg_regex: Regex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultRenderer {
|
impl DefaultRenderer {
|
||||||
pub fn new(extensions: Vec<Box<dyn Extension>>) -> DefaultRenderer {
|
pub fn new(extensions: Vec<Box<dyn Extension>>, config: Configs) -> DefaultRenderer {
|
||||||
// Register all the extensions
|
// Register all the extensions
|
||||||
let mut extension_map = HashMap::new();
|
let mut extension_map = HashMap::new();
|
||||||
for extension in extensions.into_iter() {
|
for extension in extensions.into_iter() {
|
||||||
extension_map.insert(extension.name(), extension);
|
extension_map.insert(extension.name(), extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compile the regexes
|
||||||
|
let passive_match_regex = Regex::new(&config.passive_match_regex)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!("Invalid passive match regex");
|
||||||
|
});
|
||||||
|
let passive_arg_regex = Regex::new(&config.passive_arg_regex)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!("Invalid passive arg regex");
|
||||||
|
});
|
||||||
|
|
||||||
DefaultRenderer{
|
DefaultRenderer{
|
||||||
extension_map
|
extension_map,
|
||||||
|
passive_match_regex,
|
||||||
|
passive_arg_regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +87,7 @@ impl super::Renderer for DefaultRenderer {
|
||||||
match &m.content {
|
match &m.content {
|
||||||
// Text Match
|
// Text Match
|
||||||
MatchContentType::Text(content) => {
|
MatchContentType::Text(content) => {
|
||||||
let mut target_string = if content._has_vars {
|
let target_string = if content._has_vars {
|
||||||
let mut output_map = HashMap::new();
|
let mut output_map = HashMap::new();
|
||||||
|
|
||||||
for variable in content.vars.iter() {
|
for variable in content.vars.iter() {
|
||||||
|
@ -108,6 +126,7 @@ impl super::Renderer for DefaultRenderer {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}else{ // Normal extension variables
|
}else{ // Normal extension variables
|
||||||
|
// TODO: pass the arguments to the extension
|
||||||
let extension = self.extension_map.get(&variable.var_type);
|
let extension = self.extension_map.get(&variable.var_type);
|
||||||
if let Some(extension) = extension {
|
if let Some(extension) = extension {
|
||||||
let ext_out = extension.calculate(&variable.params);
|
let ext_out = extension.calculate(&variable.params);
|
||||||
|
@ -123,6 +142,11 @@ impl super::Renderer for DefaultRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: replace the arguments
|
||||||
|
// the idea is that every param placeholder, such as $1$, is replaced with
|
||||||
|
// an ArgExtension when loading a match, which renders the argument as output
|
||||||
|
// this is only used as syntactic sugar
|
||||||
|
|
||||||
// Replace the variables
|
// Replace the variables
|
||||||
let result = VAR_REGEX.replace_all(&content.replace, |caps: &Captures| {
|
let result = VAR_REGEX.replace_all(&content.replace, |caps: &Captures| {
|
||||||
let var_name = caps.name("name").unwrap().as_str();
|
let var_name = caps.name("name").unwrap().as_str();
|
||||||
|
@ -150,6 +174,156 @@ impl super::Renderer for DefaultRenderer {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_passive(&self, text: &str, config: &Configs) -> RenderResult {
|
||||||
|
// Render the matches
|
||||||
|
let result = self.passive_match_regex.replace_all(&text, |caps: &Captures| {
|
||||||
|
let match_name = if let Some(name) = caps.name("name") {
|
||||||
|
name.as_str()
|
||||||
|
}else{
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Get the original matching string, useful to return the match untouched
|
||||||
|
let original_match = caps.get(0).unwrap().as_str();
|
||||||
|
|
||||||
|
// Find the corresponding match
|
||||||
|
let m = DefaultRenderer::find_match(config, match_name);
|
||||||
|
|
||||||
|
// If no match is found, leave the match without modifications
|
||||||
|
if m.is_none() {
|
||||||
|
return original_match.to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the args by separating them
|
||||||
|
let match_args = if let Some(args) = caps.name("args") {
|
||||||
|
args.as_str()
|
||||||
|
}else{
|
||||||
|
""
|
||||||
|
};
|
||||||
|
let args : Vec<String> = self.passive_arg_regex.split(match_args).into_iter().map(
|
||||||
|
|arg| arg.to_owned()
|
||||||
|
).collect();
|
||||||
|
|
||||||
|
let m = m.unwrap();
|
||||||
|
// Render the actual match
|
||||||
|
let result = self.render_match(&m, &config, args);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
RenderResult::Text(out) => {
|
||||||
|
out
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
original_match.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
RenderResult::Text(result.into_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tests
|
// TESTS
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn get_renderer(config: Configs) -> DefaultRenderer {
|
||||||
|
DefaultRenderer::new(crate::extension::get_extensions(), config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_config_for(s: &str) -> Configs {
|
||||||
|
let config : Configs = serde_yaml::from_str(s).unwrap();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_render(rendered: RenderResult, target: &str) {
|
||||||
|
match rendered {
|
||||||
|
RenderResult::Text(rendered) => {
|
||||||
|
println!("{}", rendered);
|
||||||
|
assert_eq!(rendered, target);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_passive_no_matches() {
|
||||||
|
let text = r###"
|
||||||
|
this text contains no matches
|
||||||
|
"###;
|
||||||
|
|
||||||
|
let config = get_config_for(r###"
|
||||||
|
matches:
|
||||||
|
- trigger: test
|
||||||
|
replace: result
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let renderer = get_renderer(config.clone());
|
||||||
|
|
||||||
|
let rendered = renderer.render_passive(text, &config);
|
||||||
|
|
||||||
|
verify_render(rendered, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_passive_simple_match_no_args() {
|
||||||
|
let text = "this is a :test";
|
||||||
|
|
||||||
|
let config = get_config_for(r###"
|
||||||
|
matches:
|
||||||
|
- trigger: ':test'
|
||||||
|
replace: result
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let renderer = get_renderer(config.clone());
|
||||||
|
|
||||||
|
let rendered = renderer.render_passive(text, &config);
|
||||||
|
|
||||||
|
verify_render(rendered, "this is a result");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_passive_multiple_match_no_args() {
|
||||||
|
let text = "this is a :test and then another :test";
|
||||||
|
|
||||||
|
let config = get_config_for(r###"
|
||||||
|
matches:
|
||||||
|
- trigger: ':test'
|
||||||
|
replace: result
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let renderer = get_renderer(config.clone());
|
||||||
|
|
||||||
|
let rendered = renderer.render_passive(text, &config);
|
||||||
|
|
||||||
|
verify_render(rendered, "this is a result and then another result");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render_passive_simple_match_multiline_no_args() {
|
||||||
|
let text = r###"this is a
|
||||||
|
:test
|
||||||
|
"###;
|
||||||
|
|
||||||
|
let result= r###"this is a
|
||||||
|
result
|
||||||
|
"###;
|
||||||
|
|
||||||
|
let config = get_config_for(r###"
|
||||||
|
matches:
|
||||||
|
- trigger: ':test'
|
||||||
|
replace: result
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let renderer = get_renderer(config.clone());
|
||||||
|
|
||||||
|
let rendered = renderer.render_passive(text, &config);
|
||||||
|
|
||||||
|
verify_render(rendered, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,11 @@ use crate::config::Configs;
|
||||||
pub(crate) mod default;
|
pub(crate) mod default;
|
||||||
|
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
|
// Render a match output
|
||||||
fn render_match(&self, m: &Match, config: &Configs, args: Vec<String>) -> RenderResult;
|
fn render_match(&self, m: &Match, config: &Configs, args: Vec<String>) -> RenderResult;
|
||||||
|
|
||||||
|
// Render a passive expansion text
|
||||||
|
fn render_passive(&self, text: &str, config: &Configs) -> RenderResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RenderResult {
|
pub enum RenderResult {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user