Support watching multiple paths

This commit is contained in:
Matt Green 2017-02-04 16:18:02 -05:00
parent cd083ef650
commit e16a6b3a24
6 changed files with 47 additions and 35 deletions

2
Cargo.lock generated
View File

@ -1,6 +1,6 @@
[root]
name = "watchexec"
version = "1.6.2"
version = "1.7.0"
dependencies = [
"clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -6,6 +6,7 @@ use clap::{App, Arg};
#[derive(Debug)]
pub struct Args {
pub cmd: String,
pub paths: Vec<String>,
pub filters: Vec<String>,
pub ignores: Vec<String>,
pub clear_screen: bool,
@ -44,6 +45,13 @@ pub fn get_args() -> Args {
.short("e")
.long("exts")
.takes_value(true))
.arg(Arg::with_name("path")
.help("Watch a specific directory")
.short("w")
.long("watch")
.number_of_values(1)
.multiple(true)
.takes_value(true))
.arg(Arg::with_name("clear")
.help("Clear screen before executing command")
.short("c")
@ -93,6 +101,8 @@ pub fn get_args() -> Args {
.get_matches();
let cmd = values_t!(args.values_of("command"), String).unwrap().join(" ");
let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);
if let Some(extensions) = args.values_of("extensions") {
@ -121,6 +131,7 @@ pub fn get_args() -> Args {
Args {
cmd: cmd,
paths: paths,
filters: filters,
ignores: ignores,
clear_screen: args.is_present("clear"),

View File

@ -41,7 +41,7 @@ enum MatchResult {
None,
}
pub fn load(paths: Vec<&Path>) -> Gitignore {
pub fn load(paths: &Vec<PathBuf>) -> Gitignore {
let mut files = vec![];
let mut checked_dirs = HashSet::new();

View File

@ -27,8 +27,7 @@ mod signal;
mod watcher;
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
@ -79,34 +78,31 @@ fn main() {
init_logger(args.debug);
let cwd = env::current_dir()
.expect("unable to get cwd")
.canonicalize()
.expect("unable to canonicalize cwd");
let paths: Vec<PathBuf> = args.paths
.iter()
.map(|p| Path::new(&p)
.canonicalize()
.expect(&format!("unable to canonicalize \"{}\"", &p))
.to_owned())
.collect();
let gitignore = if !args.no_vcs_ignore {
gitignore::load(vec![&cwd])
gitignore::load(&paths)
} else {
gitignore::load(vec![])
gitignore::load(&vec![])
};
let filter = NotificationFilter::new(args.filters, args.ignores, gitignore)
.expect("unable to create notification filter");
let (tx, rx) = channel();
let mut watcher = Watcher::new(tx, args.poll, args.poll_interval)
let watcher = Watcher::new(tx, &paths, args.poll, args.poll_interval)
.expect("unable to create watcher");
if watcher.is_polling() {
warn!("Polling for changes every {} ms", args.poll_interval);
}
let result = watcher.watch(cwd);
if let Err(e) = result {
error!("Unable to watch directory/subdirectory: {}", e);
return;
}
// Start child process initially, if necessary
if args.run_initially && !args.once {
if args.clear_screen {

View File

@ -94,7 +94,7 @@ mod tests {
#[test]
fn test_allows_everything_by_default() {
let filter = NotificationFilter::new(vec![], vec![], gitignore::load(vec![])).unwrap();
let filter = NotificationFilter::new(vec![], vec![], gitignore::load(&vec![])).unwrap();
assert!(!filter.is_excluded(&Path::new("foo")));
}
@ -102,7 +102,7 @@ mod tests {
#[test]
fn test_multiple_filters() {
let filters = vec![String::from("*.rs"), String::from("*.toml")];
let filter = NotificationFilter::new(filters, vec![], gitignore::load(vec![])).unwrap();
let filter = NotificationFilter::new(filters, vec![], gitignore::load(&vec![])).unwrap();
assert!(!filter.is_excluded(&Path::new("hello.rs")));
assert!(!filter.is_excluded(&Path::new("Cargo.toml")));
@ -112,7 +112,7 @@ mod tests {
#[test]
fn test_multiple_ignores() {
let ignores = vec![String::from("*.rs"), String::from("*.toml")];
let filter = NotificationFilter::new(vec![], ignores, gitignore::load(vec![])).unwrap();
let filter = NotificationFilter::new(vec![], ignores, gitignore::load(&vec![])).unwrap();
assert!(filter.is_excluded(&Path::new("hello.rs")));
assert!(filter.is_excluded(&Path::new("Cargo.toml")));
@ -122,7 +122,7 @@ mod tests {
#[test]
fn test_ignores_take_precedence() {
let ignores = vec![String::from("*.rs"), String::from("*.toml")];
let filter = NotificationFilter::new(ignores.clone(), ignores, gitignore::load(vec![]))
let filter = NotificationFilter::new(ignores.clone(), ignores, gitignore::load(&vec![]))
.unwrap();
assert!(filter.is_excluded(&Path::new("hello.rs")));

View File

@ -1,4 +1,4 @@
use std::path::Path;
use std::path::PathBuf;
use std::sync::mpsc::Sender;
use notify::{PollWatcher, RecommendedWatcher, RecursiveMode, raw_watcher};
@ -22,14 +22,28 @@ enum WatcherImpl {
}
impl Watcher {
pub fn new(tx: Sender<Event>, poll: bool, interval_ms: u32) -> Result<Watcher, Error> {
pub fn new(tx: Sender<Event>, paths: &Vec<PathBuf>, poll: bool, interval_ms: u32) -> Result<Watcher, Error> {
use notify::Watcher;
let imp = if poll {
WatcherImpl::Poll(try!(PollWatcher::with_delay_ms(tx, interval_ms)))
let mut watcher = try!(PollWatcher::with_delay_ms(tx, interval_ms));
for ref path in paths {
try!(watcher.watch(path, RecursiveMode::Recursive));
debug!("Watching {:?}", path);
}
WatcherImpl::Poll(watcher)
} else {
WatcherImpl::Recommended(try!(raw_watcher(tx)))
let mut watcher = try!(raw_watcher(tx));
for ref path in paths {
try!(watcher.watch(path, RecursiveMode::Recursive));
debug!("Watching {:?}", path);
}
WatcherImpl::Recommended(watcher)
};
Ok(Watcher { watcher_impl: imp })
Ok(self::Watcher { watcher_impl: imp })
}
pub fn is_polling(&self) -> bool {
@ -39,13 +53,4 @@ impl Watcher {
false
}
}
pub fn watch<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
use notify::Watcher;
match self.watcher_impl {
WatcherImpl::Recommended(ref mut w) => w.watch(path, RecursiveMode::Recursive),
WatcherImpl::Poll(ref mut w) => w.watch(path, RecursiveMode::Recursive),
}
}
}