Auto merge of #6136 - dtolnay:serve, r=flip1995

Clippy dev subcommand to build and serve website

This PR adds `clippy dev serve` which will pop open a browser with a local rendered 'ALL the Clippy Lints' website, and re-render as you edit stuff.

---

changelog: none
This commit is contained in:
bors 2020-10-09 08:58:19 +00:00
commit 2f6439ae6a
5 changed files with 87 additions and 2 deletions

View File

@ -8,6 +8,7 @@ edition = "2018"
bytecount = "0.6" bytecount = "0.6"
clap = "2.33" clap = "2.33"
itertools = "0.9" itertools = "0.9"
opener = "0.4"
regex = "1" regex = "1"
shell-escape = "0.1" shell-escape = "0.1"
walkdir = "2" walkdir = "2"

View File

@ -13,6 +13,7 @@ use walkdir::WalkDir;
pub mod fmt; pub mod fmt;
pub mod new_lint; pub mod new_lint;
pub mod ra_setup; pub mod ra_setup;
pub mod serve;
pub mod stderr_length_check; pub mod stderr_length_check;
pub mod update_lints; pub mod update_lints;

View File

@ -1,7 +1,7 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![cfg_attr(feature = "deny-warnings", deny(warnings))]
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints}; use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
fn main() { fn main() {
let matches = App::new("Clippy developer tooling") let matches = App::new("Clippy developer tooling")
@ -100,6 +100,19 @@ fn main() {
.required(true), .required(true),
), ),
) )
.subcommand(
SubCommand::with_name("serve")
.about("Launch a local 'ALL the Clippy Lints' website in a browser")
.arg(
Arg::with_name("port")
.long("port")
.short("p")
.help("Local port for the http server")
.default_value("8000")
.validator_os(serve::validate_port),
)
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
)
.get_matches(); .get_matches();
match matches.subcommand() { match matches.subcommand() {
@ -129,6 +142,11 @@ fn main() {
stderr_length_check::check(); stderr_length_check::check();
}, },
("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
("serve", Some(matches)) => {
let port = matches.value_of("port").unwrap().parse().unwrap();
let lint = matches.value_of("lint");
serve::run(port, lint);
},
_ => {}, _ => {},
} }
} }

64
clippy_dev/src/serve.rs Normal file
View File

@ -0,0 +1,64 @@
use std::ffi::{OsStr, OsString};
use std::path::Path;
use std::process::Command;
use std::thread;
use std::time::{Duration, SystemTime};
pub fn run(port: u16, lint: Option<&str>) -> ! {
let mut url = Some(match lint {
None => format!("http://localhost:{}", port),
Some(lint) => format!("http://localhost:{}/#{}", port, lint),
});
loop {
if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
Command::new("python3")
.arg("util/export.py")
.spawn()
.unwrap()
.wait()
.unwrap();
}
if let Some(url) = url.take() {
thread::spawn(move || {
Command::new("python3")
.arg("-m")
.arg("http.server")
.arg(port.to_string())
.current_dir("util/gh-pages")
.spawn()
.unwrap();
// Give some time for python to start
thread::sleep(Duration::from_millis(500));
// Launch browser after first export.py has completed and http.server is up
let _ = opener::open(url);
});
}
thread::sleep(Duration::from_millis(1000));
}
}
fn mtime(path: impl AsRef<Path>) -> SystemTime {
let path = path.as_ref();
if path.is_dir() {
path.read_dir()
.into_iter()
.flatten()
.flatten()
.map(|entry| mtime(&entry.path()))
.max()
.unwrap_or(SystemTime::UNIX_EPOCH)
} else {
path.metadata()
.and_then(|metadata| metadata.modified())
.unwrap_or(SystemTime::UNIX_EPOCH)
}
}
#[allow(clippy::missing_errors_doc)]
pub fn validate_port(arg: &OsStr) -> Result<(), OsString> {
match arg.to_string_lossy().parse::<u16>() {
Ok(_port) => Ok(()),
Err(err) => Err(OsString::from(err.to_string())),
}
}

View File

@ -189,7 +189,8 @@ declare_clippy_lint! {
* The section of lines prefixed with `///` constitutes the lint documentation * The section of lines prefixed with `///` constitutes the lint documentation
section. This is the default documentation style and will be displayed section. This is the default documentation style and will be displayed
[like this][example_lint_page]. [like this][example_lint_page]. To render and open this documentation locally
in a browser, run `cargo dev serve`.
* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
[lint naming guidelines][lint_naming] here when naming your lint. [lint naming guidelines][lint_naming] here when naming your lint.
In short, the name should state the thing that is being checked for and In short, the name should state the thing that is being checked for and