Watch builds are now spawned in new threads (using crossbeam) and there is a timelock, preventing multiple builds being triggered in less than a second
This commit is contained in:
parent
522eef9296
commit
cdbb2ee5fd
16
Cargo.toml
16
Cargo.toml
|
@ -19,20 +19,28 @@ clap = "*"
|
||||||
handlebars = "*"
|
handlebars = "*"
|
||||||
rustc-serialize = "*"
|
rustc-serialize = "*"
|
||||||
pulldown-cmark = "*"
|
pulldown-cmark = "*"
|
||||||
|
crossbeam = "^0.1.5"
|
||||||
|
|
||||||
|
# Watch feature
|
||||||
|
[dependencies.notify]
|
||||||
|
notify = "^2.4.1"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.time]
|
||||||
|
time = "^0.1.33"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
# Tests
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempdir = "*"
|
tempdir = "*"
|
||||||
|
|
||||||
[dependencies.notify]
|
|
||||||
notify = "*"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["output", "watch"]
|
default = ["output", "watch"]
|
||||||
debug = []
|
debug = []
|
||||||
output = []
|
output = []
|
||||||
regenerate-css = []
|
regenerate-css = []
|
||||||
watch = ["notify"]
|
watch = ["notify", "time"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
doc = false
|
doc = false
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
- [Command Line Tool](cli/cli-tool.md)
|
- [Command Line Tool](cli/cli-tool.md)
|
||||||
- [init](cli/init.md)
|
- [init](cli/init.md)
|
||||||
- [build](cli/build.md)
|
- [build](cli/build.md)
|
||||||
- [watch]()
|
- [watch](cli/watch.md)
|
||||||
- [Format](format/format.md)
|
- [Format](format/format.md)
|
||||||
- [SUMMARY.md](format/summary.md)
|
- [SUMMARY.md](format/summary.md)
|
||||||
- [Configuration](format/config.md)
|
- [Configuration](format/config.md)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# The watch command
|
||||||
|
|
||||||
|
The watch command is useful when you want your book to be rendered on every file change.
|
||||||
|
You could issue `mdbook build` everytime you change a file. But using `mdbook watch` once will watch your files and will trigger a build whenever you modify a file.
|
||||||
|
|
||||||
|
#### Specify a directory
|
||||||
|
|
||||||
|
Like `init` and `build`, `watch` can take a directory as argument to use instead of the
|
||||||
|
current working directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
mdbook watch path/to/book
|
||||||
|
```
|
|
@ -1,8 +1,14 @@
|
||||||
extern crate mdbook;
|
extern crate mdbook;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
extern crate crossbeam;
|
||||||
|
|
||||||
|
// Dependencies for the Watch feature
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
#[cfg(feature = "watch")]
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
@ -11,11 +17,13 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use clap::{App, ArgMatches, SubCommand};
|
use clap::{App, ArgMatches, SubCommand};
|
||||||
|
|
||||||
|
// Uses for the Watch feature
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use notify::Watcher;
|
use notify::Watcher;
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
|
||||||
const NAME: &'static str = "mdbook";
|
const NAME: &'static str = "mdbook";
|
||||||
|
@ -57,6 +65,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Simple function that user comfirmation
|
||||||
fn confirm() -> bool {
|
fn confirm() -> bool {
|
||||||
io::stdout().flush().unwrap();
|
io::stdout().flush().unwrap();
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
|
@ -67,6 +77,8 @@ fn confirm() -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Init command implementation
|
||||||
fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
|
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
|
@ -104,6 +116,8 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Build command implementation
|
||||||
fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
|
fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::new(&book_dir).read_config();
|
let mut book = MDBook::new(&book_dir).read_config();
|
||||||
|
@ -113,6 +127,8 @@ fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Watch command implementation
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
|
@ -127,25 +143,39 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
Ok(mut watcher) => {
|
Ok(mut watcher) => {
|
||||||
|
|
||||||
watcher.watch(book.get_src()).unwrap();
|
watcher.watch(book.get_src()).unwrap();
|
||||||
|
watcher.watch(book_dir.join("book.json")).unwrap();
|
||||||
|
|
||||||
loop {
|
let previous_time = time::get_time().sec;
|
||||||
match rx.recv() {
|
|
||||||
Ok(event) => {
|
crossbeam::scope(|scope| {
|
||||||
if let Some(path) = event.path {
|
loop {
|
||||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
match rx.recv() {
|
||||||
try!(build(args));
|
Ok(event) => {
|
||||||
println!("");
|
|
||||||
// Hack to prevent receiving the event 4 times, probably a bug in notify
|
// Skip the event if an event has already been issued in the last second
|
||||||
return watch(args);
|
if time::get_time().sec - previous_time < 1 { continue }
|
||||||
} else {
|
|
||||||
continue;
|
if let Some(path) = event.path {
|
||||||
|
// Trigger the build process in a new thread (to keep receiving events)
|
||||||
|
scope.spawn(move || {
|
||||||
|
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||||
|
match build(args) {
|
||||||
|
Err(e) => println!("Error while building: {:?}", e),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("An error occured: {:?}", e);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
println!("An error occured: {:?}", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -158,6 +188,7 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helper function that returns the right path if either a relative or absolute path is passed
|
||||||
fn get_book_dir(args: &ArgMatches) -> PathBuf {
|
fn get_book_dir(args: &ArgMatches) -> PathBuf {
|
||||||
if let Some(dir) = args.value_of("dir") {
|
if let Some(dir) = args.value_of("dir") {
|
||||||
// Check if path is relative from current dir, or absolute...
|
// Check if path is relative from current dir, or absolute...
|
||||||
|
|
Loading…
Reference in New Issue