diff --git a/src/main.rs b/src/main.rs index 7d94219..18ffede 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,15 +21,17 @@ fn main() -> Result<(), Box> { debug!{"Set up roftl"}; let mut roftl = roftl::Roftl::new() .add_source(sources::TestSource::new("ts1")) - .add_source(sources::Window::new()) .add_source(sources::Apps::new()) .add_source(sources::ShellHost::new()) .with_matcher(FuseMatcher::new()) + .with_primary_source(sources::Window::new()) .with_primary_matcher(PrefixMatcher::new()); + debug!{"Source primary roftl sources"} + roftl.source_primary(); - debug!{"Source roftl sources"} - roftl.source(); + debug!{"Source additional roftl sources"} + roftl.source_additional(); debug!{"Build window"} let event_loop = EventLoop::new(); @@ -41,8 +43,8 @@ fn main() -> Result<(), Box> { debug!{"Window id: {:?}", window.id()} - debug!{"Draw empty state to window"} - ui::draw(&window, "", vec![], 0); + debug!{"Draw primary state to window"} + ui::draw(&window, "", roftl.narrow(""), 0); debug!{"Start event loop"} let roftl_loop = RoftlLoop::new(roftl, window); diff --git a/src/roftl.rs b/src/roftl.rs index 45c7f7f..785ded8 100644 --- a/src/roftl.rs +++ b/src/roftl.rs @@ -1,3 +1,6 @@ +use std::rc::Rc; +use std::sync::RwLock; +use std::sync::Arc; use log::debug; use rayon::prelude::*; @@ -31,7 +34,9 @@ pub trait Matcher: Send + Sync { } pub struct Roftl { - sources: Vec, + sources: Arc>>, + source_chan: Option>>, + primary_source: Option, matcher: MatcherRef, // the standard matcher can be inadequate for primary mode. E.g. a // FuseMatcher would yield hard to guess and sometimes surprising results, @@ -45,7 +50,9 @@ pub struct Roftl { impl Roftl { pub fn new() -> Self { Roftl { - sources: vec![], + sources: Arc::from(RwLock::from(vec![])), + source_chan: None, + primary_source: None, matcher: PrefixMatcher::new(), primary_matcher: PrefixMatcher::new(), entries: vec![], @@ -53,12 +60,18 @@ impl Roftl { } } + pub fn with_primary_source(mut self, source: SourceRef) -> Self { + self.primary_source = Some(source); + + self + } + pub fn add_source(mut self, source: SourceRef) -> Self { - if self.sources.par_iter().any(|s| s.name().eq(source.name())) { + if self.sources.read().unwrap().par_iter().any(|s| s.name().eq(source.name())) { panic! {"Source with name '{}' already exists", source.name()} } - self.sources.push(source); + self.sources.write().unwrap().push(source); self } @@ -72,13 +85,29 @@ impl Roftl { self } - pub fn source(&mut self) { - self.entries = self.sources + pub fn source_primary(&mut self) { + self.entries = self.primary_source .par_iter_mut() .flat_map(|s| s.entries()) .collect(); - debug!("Sourced {} entries from {} sources", self.entries.len(), self.sources.len()); + debug!("Sourced {} primary entries", self.entries.len()); + } + + pub fn source_additional(&mut self) { + let (tx, rx) = std::sync::mpsc::channel(); + self.source_chan = Some(rx); + + let sources = self.sources.clone(); + std::thread::spawn(move || { + let entries: Vec = sources.write().unwrap() + .par_iter_mut() + .flat_map(|s| s.entries()) + .collect(); + + debug!("Sourced {} additional entries from {} sources", entries.len(), sources.read().unwrap().len()); + tx.send(entries).unwrap(); + }); } pub fn narrow(&mut self, input: &str) -> Vec<(&Entry, Vec)> { @@ -86,6 +115,19 @@ impl Roftl { completions.insert("e", "emacs"); completions.insert("f", "firefox"); + // check if we received anything in self. + if let Some(ref rx) = self.source_chan { + if let Ok(mut entries) = rx.try_recv() { + debug!("Processing {} additional entries", entries.len()); + self.entries.append(&mut entries); + } else { + debug!("No additional entries for processing (yet)"); + } + } + + // we need to primary_matcher here s.t. only the primary matcher + // and not self is captured by the filter_map lambda + let primary_matcher = &self.primary_matcher; let mut scored_entries: Vec<(f64, usize, &Entry, Vec)> = self.entries .par_iter() .enumerate() @@ -96,20 +138,25 @@ impl Roftl { let input = completions.get(input).unwrap_or(&input); - let match_result = self.primary_matcher.try_match(&entry.name, input); + let match_result = primary_matcher.try_match(&entry.name, input); return match_result.map(|(score, indices)| (score, idx, entry, indices) ) }) .collect(); + // we need to matcher here s.t. only the matcher + // and not self is captured by the filter_map lambda if scored_entries.is_empty() || input.starts_with(",") { let input = input.strip_prefix(",").unwrap_or(input); + let matcher = &self.matcher; + scored_entries = self.entries .par_iter() .enumerate() .filter_map(|(idx, entry)| { + if entry.source == "window" { return None } if input.is_empty() { return Some((0.0, idx, entry, vec![])) } - let match_result = self.matcher.try_match(&entry.name, input); + let match_result = matcher.try_match(&entry.name, input); return match_result.map(|(score, indices)| (score, idx, entry, indices) ) }) .collect(); @@ -131,30 +178,46 @@ impl Roftl { pub fn actions(&self, selection_id: usize) -> Vec { let (entry, source) = self.find_selection(selection_id); - source.actions(entry) + match source { + usize::MAX => self.primary_source.as_ref().unwrap().actions(entry), + _ => self.sources.read().unwrap().get(source).unwrap().actions(entry) + } } pub fn exec_default(&self, selection_id: usize) { let (entry, source) = self.find_selection(selection_id); - source.exec_default(entry); + match source { + usize::MAX => self.primary_source.as_ref().unwrap().exec_default(entry), + _ => self.sources.read().unwrap().get(source).unwrap().exec_default(entry) + } } pub fn exec_action(&self, selection_id: usize, action: u8) { let (entry, source) = self.find_selection(selection_id); - source.exec_action(entry, action); + match source { + usize::MAX => self.primary_source.as_ref().unwrap().exec_action(entry, action), + _ => self.sources.read().unwrap().get(source).unwrap().exec_action(entry, action) + } } - fn find_selection(&self, selection_id: usize) -> (&Entry, &Box) + fn find_selection(&self, selection_id: usize) -> (&Entry, usize) { let entry_id = self.narrow_map[selection_id]; let entry = &self.entries[entry_id]; debug!{"Got entry {:?} for id {} ", entry, entry_id}; - let source = self.sources + match self.primary_source { + Some(ref s) if s.name().eq(entry.source) => return (entry, usize::MAX), + _ => {} + } + + let sources = self.sources.read().unwrap(); + let source = sources .iter() - .find(|s| s.name().eq(entry.source)) + .enumerate() + .find(|(_i, s)| s.name().eq(entry.source)) .unwrap(); - (entry, source) + (entry, source.0) } }