From 85ab4d39cd0fd6fc09903edc32621adab068ec31 Mon Sep 17 00:00:00 2001 From: Ruin0x11 Date: Fri, 28 Aug 2020 01:36:22 -0700 Subject: [PATCH] Redirect to translation index page in serve command --- Cargo.lock | 1 + Cargo.toml | 1 + src/cmd/serve.rs | 32 +++++++++-- src/theme/book.js | 138 +++++++++++++++++++++++----------------------- 4 files changed, 100 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f723808..06edc9a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -789,6 +789,7 @@ dependencies = [ "futures-util", "gitignore", "handlebars", + "http", "lazy_static", "log", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 3646a7ea..23fb59c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ chrono = "0.4" clap = "2.24" env_logger = "0.7.1" handlebars = "4.0" +http = "0.2.4" lazy_static = "1.0" log = "0.4" memchr = "2.0" diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index ff2fc480..bb1eb819 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -4,6 +4,7 @@ use crate::{get_book_dir, get_build_opts, open}; use clap::{App, Arg, ArgMatches, SubCommand}; use futures_util::sink::SinkExt; use futures_util::StreamExt; +use http::Uri; use mdbook::errors::*; use mdbook::utils; use mdbook::utils::fs::get_404_output_file; @@ -60,7 +61,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); let build_opts = get_build_opts(args); - let mut book = MDBook::load_with_build_opts(&book_dir, build_opts)?; + let mut book = MDBook::load_with_build_opts(&book_dir, build_opts.clone())?; let port = args.value_of("port").unwrap(); let hostname = args.value_of("hostname").unwrap(); @@ -82,6 +83,18 @@ pub fn execute(args: &ArgMatches) -> Result<()> { update_config(&mut book); book.build()?; + let language: Option = match build_opts.language_ident { + // index.html will be at the root directory. + Some(_) => None, + None => match book.config.language.default_language() { + // If book has translations, index.html will be under src/en/ or + // similar. + Some(lang_ident) => Some(lang_ident.clone()), + // If not, it will be at the root. + None => None, + } + }; + let sockaddr: SocketAddr = address .to_socket_addrs()? .next() @@ -100,7 +113,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let reload_tx = tx.clone(); let thread_handle = std::thread::spawn(move || { - serve(build_dir, sockaddr, reload_tx, &file_404); + serve(build_dir, sockaddr, reload_tx, &file_404, language); }); let serving_url = format!("http://{}", address); @@ -140,6 +153,7 @@ async fn serve( address: SocketAddr, reload_tx: broadcast::Sender, file_404: &str, + language: Option, ) { // A warp Filter which captures `reload_tx` and provides an `rx` copy to // receive reload messages. @@ -166,7 +180,6 @@ async fn serve( // The fallback route for 404 errors let fallback_route = warp::fs::file(build_dir.join(file_404)) .map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND)); - let routes = livereload.or(book_route).or(fallback_route); std::panic::set_hook(Box::new(move |panic_info| { // exit if serve panics @@ -174,5 +187,16 @@ async fn serve( std::process::exit(1); })); - warp::serve(routes).run(address).await; + if let Some(lang_ident) = language { + // Redirect root to the default translation directory, if serving a localized book. + // BUG: This can't be `/{lang_ident}`, or the static assets won't get loaded. + let index_for_language = format!("/{}/index.html", lang_ident).parse::().unwrap(); + let redirect_to_index = warp::path::end().map(move || warp::redirect(index_for_language.clone())); + let routes = livereload.or(redirect_to_index).or(book_route).or(fallback_route); + warp::serve(routes).run(address).await; + } + else { + let routes = livereload.or(book_route).or(fallback_route); + warp::serve(routes).run(address).await; + }; } diff --git a/src/theme/book.js b/src/theme/book.js index 12785740..ff03ab69 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -428,81 +428,83 @@ function playground_text(playground) { var languageToggleButton = document.getElementById('language-toggle'); var languagePopup = document.getElementById('language-list'); - function showLanguages() { - languagePopup.style.display = 'block'; - languageToggleButton.setAttribute('aria-expanded', true); - } - - function hideLanguages() { - languagePopup.style.display = 'none'; - languageToggleButton.setAttribute('aria-expanded', false); - languageToggleButton.focus(); - } - - function set_language(language) { - console.log("Set language " + language) - } - - languageToggleButton.addEventListener('click', function () { - if (languagePopup.style.display === 'block') { - hideLanguages(); - } else { - showLanguages(); + if (languageToggleButton !== null) { + function showLanguages() { + languagePopup.style.display = 'block'; + languageToggleButton.setAttribute('aria-expanded', true); } - }); - languagePopup.addEventListener('click', function (e) { - var language = e.target.id || e.target.parentElement.id; - set_language(language); - }); - - languagePopup.addEventListener('focusout', function(e) { - // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) - if (!!e.relatedTarget && !languageToggleButton.contains(e.relatedTarget) && !languagePopup.contains(e.relatedTarget)) { - hideLanguages(); + function hideLanguages() { + languagePopup.style.display = 'none'; + languageToggleButton.setAttribute('aria-expanded', false); + languageToggleButton.focus(); } - }); - // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 - document.addEventListener('click', function(e) { - if (languagePopup.style.display === 'block' && !languageToggleButton.contains(e.target) && !languagePopup.contains(e.target)) { - hideLanguages(); + function set_language(language) { + console.log("Set language " + language) } - }); - document.addEventListener('keydown', function (e) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } - if (!languagePopup.contains(e.target)) { return; } - - switch (e.key) { - case 'Escape': - e.preventDefault(); + languageToggleButton.addEventListener('click', function () { + if (languagePopup.style.display === 'block') { hideLanguages(); - break; - case 'ArrowUp': - e.preventDefault(); - var li = document.activeElement.parentElement; - if (li && li.previousElementSibling) { - li.previousElementSibling.querySelector('button').focus(); - } - break; - case 'ArrowDown': - e.preventDefault(); - var li = document.activeElement.parentElement; - if (li && li.nextElementSibling) { - li.nextElementSibling.querySelector('button').focus(); - } - break; - case 'Home': - e.preventDefault(); - languagePopup.querySelector('li:first-child button').focus(); - break; - case 'End': - e.preventDefault(); - languagePopup.querySelector('li:last-child button').focus(); - break; - } - }); + } else { + showLanguages(); + } + }); + + languagePopup.addEventListener('click', function (e) { + var language = e.target.id || e.target.parentElement.id; + set_language(language); + }); + + languagePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !languageToggleButton.contains(e.relatedTarget) && !languagePopup.contains(e.relatedTarget)) { + hideLanguages(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (languagePopup.style.display === 'block' && !languageToggleButton.contains(e.target) && !languagePopup.contains(e.target)) { + hideLanguages(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!languagePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideLanguages(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + languagePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + languagePopup.querySelector('li:last-child button').focus(); + break; + } + }); + } })(); (function sidebar() {