Made `mdbook watch` and `mdbook serve` rebuild the book again (#508)

Made `mdbook watch` and `mdbook serve` rebuild the book again
This commit is contained in:
Michael Bryan 2017-12-13 12:04:24 +11:00 committed by GitHub
parent d69bc9c7c3
commit 396426662d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 73 deletions

View File

@ -3,12 +3,11 @@ extern crate staticfile;
extern crate ws;
use std;
use std::path::PathBuf;
use self::iron::{status, AfterMiddleware, Chain, Iron, IronError, IronResult, Request, Response,
Set};
use clap::{App, ArgMatches, SubCommand};
use mdbook::MDBook;
use mdbook::errors::Result;
use mdbook::errors::*;
use {get_book_dir, open};
#[cfg(feature = "watch")]
use watch;
@ -18,28 +17,21 @@ struct ErrorRecover;
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("serve")
.about(
"Serve the book at http://localhost:3000. Rebuild and reload on change.",
)
.about("Serve the book at http://localhost:3000. Rebuild and reload on change.")
.arg_from_usage(
"[dir] 'A directory for your book{n}(Defaults to \
Current Directory when omitted)'",
)
.arg_from_usage(
"-d, --dest-dir=[dest-dir] 'The output directory for \
your book{n}(Defaults to ./book when omitted)'",
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
)
.arg_from_usage("-p, --port=[port] 'Use another port{n}(Defaults to 3000)'")
.arg_from_usage(
"-w, --websocket-port=[ws-port] 'Use another port for the \
websocket connection (livereload){n}(Defaults to 3001)'",
"-w, --websocket-port=[ws-port] 'Use another port for the websocket connection \
(livereload){n}(Defaults to 3001)'",
)
.arg_from_usage(
"-i, --interface=[interface] 'Interface to listen on{n}(Defaults to localhost)'",
)
.arg_from_usage(
"-a, --address=[address] 'Address that the browser can reach the \
websocket server from{n}(Defaults to the interface address)'",
"-a, --address=[address] 'Address that the browser can reach the websocket server \
from{n}(Defaults to the interface address)'",
)
.arg_from_usage("-o, --open 'Open the book server in a web browser'")
}
@ -51,10 +43,6 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let mut book = MDBook::load(&book_dir)?;
if let Some(dest_dir) = args.value_of("dest-dir") {
book.config.build.build_dir = PathBuf::from(dest_dir);
}
let port = args.value_of("port").unwrap_or("3000");
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
let interface = args.value_of("interface").unwrap_or("localhost");
@ -80,18 +68,19 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
}}
</script>
"#,
public_address,
ws_port,
RELOAD_COMMAND
public_address, ws_port, RELOAD_COMMAND
));
book.build()?;
let mut chain = Chain::new(staticfile::Static::new(book.get_destination()));
chain.link_after(ErrorRecover);
let _iron = Iron::new(chain).http(&*address).unwrap();
let _iron = Iron::new(chain)
.http(&*address)
.chain_err(|| "Unable to launch the server")?;
let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap();
let ws_server =
ws::WebSocket::new(|_| |_| Ok(())).chain_err(|| "Unable to start the websocket")?;
let broadcaster = ws_server.broadcaster();
@ -107,9 +96,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
}
#[cfg(feature = "watch")]
watch::trigger_on_change(&mut book, move |path, book| {
watch::trigger_on_change(&mut book, move |path, book_dir| {
println!("File changed: {:?}\nBuilding book...\n", path);
match book.build() {
match MDBook::load(&book_dir).and_then(|mut b| b.build()) {
Err(e) => println!("Error while building: {:?}", e),
_ => broadcaster.send(RELOAD_COMMAND).unwrap(),
}

View File

@ -1,6 +1,6 @@
extern crate notify;
use std::path::{Path, PathBuf};
use std::path::Path;
use self::notify::Watcher;
use std::time::Duration;
use std::sync::mpsc::channel;
@ -15,12 +15,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
.about("Watch the files for changes")
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] 'The output directory for \
your book{n}(Defaults to ./book when omitted)'",
)
.arg_from_usage(
"[dir] 'A directory for your book{n}(Defaults to \
Current Directory when omitted)'",
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
)
}
@ -29,30 +24,28 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let mut book = MDBook::load(&book_dir)?;
if let Some(dest_dir) = args.value_of("dest-dir") {
book.config.build.build_dir = PathBuf::from(dest_dir);
}
if args.is_present("open") {
book.build()?;
open(book.get_destination().join("index.html"));
}
trigger_on_change(&mut book, |path, book| {
trigger_on_change(&book, |path, book_dir| {
println!("File changed: {:?}\nBuilding book...\n", path);
if let Err(e) = book.build() {
println!("Error while building: {:?}", e);
let result = MDBook::load(&book_dir).and_then(|mut b| b.build());
if let Err(e) = result {
println!("Error while building: {}", e);
}
println!("");
println!();
});
Ok(())
}
// Calls the closure when a book source file is changed. This is blocking!
pub fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
/// Calls the closure when a book source file is changed, blocking indefinitely.
pub fn trigger_on_change<F>(book: &MDBook, closure: F)
where
F: Fn(&Path, &mut MDBook) -> (),
F: Fn(&Path, &Path),
{
use self::notify::RecursiveMode::*;
use self::notify::DebouncedEvent::*;
@ -64,48 +57,29 @@ where
Ok(w) => w,
Err(e) => {
println!("Error while trying to watch the files:\n\n\t{:?}", e);
::std::process::exit(0)
::std::process::exit(1)
}
};
// Add the source directory to the watcher
if let Err(e) = watcher.watch(book.source_dir(), Recursive) {
println!("Error while watching {:?}:\n {:?}", book.source_dir(), e);
::std::process::exit(0);
::std::process::exit(1);
};
// Add the theme directory to the watcher
watcher.watch(book.theme_dir(), Recursive)
.unwrap_or_default();
let _ = watcher.watch(book.theme_dir(), Recursive);
// Add the book.{json,toml} file to the watcher if it exists, because it's not
// located in the source directory
if watcher.watch(book.root.join("book.json"), NonRecursive)
.is_err()
{
// do nothing if book.json is not found
}
if watcher.watch(book.root.join("book.toml"), NonRecursive)
.is_err()
{
// do nothing if book.toml is not found
}
// Add the book.toml file to the watcher if it exists
let _ = watcher.watch(book.root.join("book.toml"), NonRecursive);
println!("\nListening for changes...\n");
loop {
match rx.recv() {
Ok(event) => {
match event {
Create(path) | Write(path) | Remove(path) | Rename(_, path) => {
closure(&path, book);
}
_ => {}
}
}
Err(e) => {
println!("An error occured: {:?}", e);
for event in rx.recv() {
match event {
Create(path) | Write(path) | Remove(path) | Rename(_, path) => {
closure(&path, &book.root);
}
_ => {}
}
}
}