First steps in passive match rendering

This commit is contained in:
Federico Terzi 2020-01-10 23:29:21 +01:00
parent 6eec895b21
commit 0eb58704a9
4 changed files with 195 additions and 6 deletions

View File

@ -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_toggle_interval() -> u32 { 230 }
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_exclude_default_matches() -> bool {false}
fn default_matches() -> Vec<Match> { Vec::new() }
@ -102,6 +104,12 @@ pub struct Configs {
#[serde(default = "default_preserve_clipboard")]
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)]
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.use_system_agent, default_use_system_agent());
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
}

View File

@ -32,7 +32,7 @@ use fs2::FileExt;
use log::{info, warn, LevelFilter};
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger, WriteLogger};
use crate::config::ConfigSet;
use crate::config::{ConfigSet, ConfigManager};
use crate::config::runtime::RuntimeConfigManager;
use crate::engine::Engine;
use crate::event::*;
@ -333,7 +333,8 @@ fn daemon_background(receive_channel: Receiver<Event>, config_set: ConfigSet) {
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,
&clipboard_manager,

View File

@ -33,18 +33,36 @@ lazy_static! {
pub struct DefaultRenderer {
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 {
pub fn new(extensions: Vec<Box<dyn Extension>>) -> DefaultRenderer {
pub fn new(extensions: Vec<Box<dyn Extension>>, config: Configs) -> DefaultRenderer {
// Register all the extensions
let mut extension_map = HashMap::new();
for extension in extensions.into_iter() {
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{
extension_map
extension_map,
passive_match_regex,
passive_arg_regex
}
}
@ -69,7 +87,7 @@ impl super::Renderer for DefaultRenderer {
match &m.content {
// Text Match
MatchContentType::Text(content) => {
let mut target_string = if content._has_vars {
let target_string = if content._has_vars {
let mut output_map = HashMap::new();
for variable in content.vars.iter() {
@ -108,6 +126,7 @@ impl super::Renderer for DefaultRenderer {
},
}
}else{ // Normal extension variables
// TODO: pass the arguments to the extension
let extension = self.extension_map.get(&variable.var_type);
if let Some(extension) = extension {
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
let result = VAR_REGEX.replace_all(&content.replace, |caps: &Captures| {
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);
}
}

View File

@ -24,7 +24,11 @@ use crate::config::Configs;
pub(crate) mod default;
pub trait Renderer {
// Render a match output
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 {