about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-10-09 08:58:19 +0000
committerbors <bors@rust-lang.org>2020-10-09 08:58:19 +0000
commit2f6439ae6a6803d030cceb3ee14c9150e91b328b (patch)
tree4f5c8df2b05103c12d328dd53418768d08862664
parent6fb3b2c1601662ac85d1e26ee4c939a7ecf680fc (diff)
parente6a71066c8e9c0aaaabd10923f73c81285b16932 (diff)
downloadrust-2f6439ae6a6803d030cceb3ee14c9150e91b328b.tar.gz
rust-2f6439ae6a6803d030cceb3ee14c9150e91b328b.zip
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
-rw-r--r--clippy_dev/Cargo.toml1
-rw-r--r--clippy_dev/src/lib.rs1
-rw-r--r--clippy_dev/src/main.rs20
-rw-r--r--clippy_dev/src/serve.rs64
-rw-r--r--doc/adding_lints.md3
5 files changed, 87 insertions, 2 deletions
diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml
index d745000eac7..b8a4a20114b 100644
--- a/clippy_dev/Cargo.toml
+++ b/clippy_dev/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
 bytecount = "0.6"
 clap = "2.33"
 itertools = "0.9"
+opener = "0.4"
 regex = "1"
 shell-escape = "0.1"
 walkdir = "2"
diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs
index 567831354f5..43cb2954b74 100644
--- a/clippy_dev/src/lib.rs
+++ b/clippy_dev/src/lib.rs
@@ -13,6 +13,7 @@ use walkdir::WalkDir;
 pub mod fmt;
 pub mod new_lint;
 pub mod ra_setup;
+pub mod serve;
 pub mod stderr_length_check;
 pub mod update_lints;
 
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 281037ae37c..7a8cbd5251d 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -1,7 +1,7 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 
 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() {
     let matches = App::new("Clippy developer tooling")
@@ -100,6 +100,19 @@ fn main() {
                         .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();
 
     match matches.subcommand() {
@@ -129,6 +142,11 @@ fn main() {
             stderr_length_check::check();
         },
         ("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);
+        },
         _ => {},
     }
 }
diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs
new file mode 100644
index 00000000000..a46c0e4d3f0
--- /dev/null
+++ b/clippy_dev/src/serve.rs
@@ -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())),
+    }
+}
diff --git a/doc/adding_lints.md b/doc/adding_lints.md
index 21e0f6f4fc7..2869c3bf7d4 100644
--- a/doc/adding_lints.md
+++ b/doc/adding_lints.md
@@ -189,7 +189,8 @@ declare_clippy_lint! {
 
 * The section of lines prefixed with `///` constitutes the lint documentation
   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
   [lint naming guidelines][lint_naming] here when naming your lint.
   In short, the name should state the thing that is being checked for and