about summary refs log tree commit diff
path: root/xtask/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-05-31 10:50:11 +0000
committerGitHub <noreply@github.com>2020-05-31 10:50:11 +0000
commit09df51dab89340bcf4b8ede95c02c32b0c8eb2bc (patch)
tree0240c629fe96243e1a1c91ccd679947bfb1ecb03 /xtask/src
parent5f7225446e75509ae0d971a6f3e2b9d3e37d6f2a (diff)
parent13a996f3b68c175f6e6ad8d89081e45850dc5583 (diff)
downloadrust-09df51dab89340bcf4b8ede95c02c32b0c8eb2bc.tar.gz
rust-09df51dab89340bcf4b8ede95c02c32b0c8eb2bc.zip
Merge #4664
4664: Generate feature documentation from code r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
Diffstat (limited to 'xtask/src')
-rw-r--r--xtask/src/codegen.rs27
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs10
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs88
-rw-r--r--xtask/src/main.rs1
4 files changed, 114 insertions, 12 deletions
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index b4907f4b2b0..f47d54125f3 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -8,14 +8,15 @@
 mod gen_syntax;
 mod gen_parser_tests;
 mod gen_assists_docs;
+mod gen_feature_docs;
 
 use std::{mem, path::Path};
 
 use crate::{not_bash::fs2, Result};
 
 pub use self::{
-    gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
-    gen_syntax::generate_syntax,
+    gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs,
+    gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax,
 };
 
 const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
@@ -40,7 +41,7 @@ pub enum Mode {
 /// With verify = false,
 fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
     match fs2::read_to_string(path) {
-        Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
+        Ok(old_contents) if normalize(&old_contents) == normalize(contents) => {
             return Ok(());
         }
         _ => (),
@@ -61,8 +62,24 @@ fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
     do_extract_comment_blocks(text, false)
 }
 
-fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> {
-    do_extract_comment_blocks(text, true)
+fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> {
+    assert!(tag.starts_with(char::is_uppercase));
+    let tag = format!("{}:", tag);
+    let mut res = Vec::new();
+    for mut block in do_extract_comment_blocks(text, true) {
+        let first = block.remove(0);
+        if first.starts_with(&tag) {
+            let id = first[tag.len()..].trim().to_string();
+            let block = CommentBlock { id, contents: block };
+            res.push(block);
+        }
+    }
+    res
+}
+
+struct CommentBlock {
+    id: String,
+    contents: Vec<String>,
 }
 
 fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> {
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 20dcde82068..6ebeb8aea4e 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -33,22 +33,18 @@ impl Assist {
 
         fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
             let text = fs::read_to_string(path)?;
-            let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
+            let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text);
 
             for block in comment_blocks {
                 // FIXME: doesn't support blank lines yet, need to tweak
                 // `extract_comment_blocks` for that.
-                let mut lines = block.iter();
-                let first_line = lines.next().unwrap();
-                if !first_line.starts_with("Assist: ") {
-                    continue;
-                }
-                let id = first_line["Assist: ".len()..].to_string();
+                let id = block.id;
                 assert!(
                     id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
                     "invalid assist id: {:?}",
                     id
                 );
+                let mut lines = block.contents.iter();
 
                 let doc = take_until(lines.by_ref(), "```").trim().to_string();
                 assert!(
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
new file mode 100644
index 00000000000..dbe583e8e4f
--- /dev/null
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -0,0 +1,88 @@
+//! Generates `assists.md` documentation.
+
+use std::{fmt, fs, path::PathBuf};
+
+use crate::{
+    codegen::{self, extract_comment_blocks_with_empty_lines, Mode},
+    project_root, rust_files, Result,
+};
+
+pub fn generate_feature_docs(mode: Mode) -> Result<()> {
+    let features = Feature::collect()?;
+    let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
+    let contents = contents.trim().to_string() + "\n";
+    let dst = project_root().join("docs/user/generated_features.adoc");
+    codegen::update(&dst, &contents, mode)?;
+    Ok(())
+}
+
+#[derive(Debug)]
+struct Feature {
+    id: String,
+    path: PathBuf,
+    doc: String,
+}
+
+impl Feature {
+    fn collect() -> Result<Vec<Feature>> {
+        let mut res = Vec::new();
+        for path in rust_files(&project_root()) {
+            collect_file(&mut res, path)?;
+        }
+        res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
+        return Ok(res);
+
+        fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> Result<()> {
+            let text = fs::read_to_string(&path)?;
+            let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text);
+
+            for block in comment_blocks {
+                let id = block.id;
+                assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
+                let doc = block.contents.join("\n");
+                acc.push(Feature { id, path: path.clone(), doc })
+            }
+
+            Ok(())
+        }
+    }
+}
+
+fn is_valid_feature_name(feature: &str) -> bool {
+    'word: for word in feature.split_whitespace() {
+        for &short in ["to", "and"].iter() {
+            if word == short {
+                continue 'word;
+            }
+        }
+        for &short in ["To", "And"].iter() {
+            if word == short {
+                return false;
+            }
+        }
+        if !word.starts_with(char::is_uppercase) {
+            return false;
+        }
+    }
+    true
+}
+
+impl fmt::Display for Feature {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        writeln!(f, "=== {}", self.id)?;
+        let path = self.path.strip_prefix(&project_root()).unwrap().display().to_string();
+        let path = path.replace('\\', "/");
+        let name = self.path.file_name().unwrap();
+
+        //FIXME: generate line number as well
+        writeln!(
+            f,
+            "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]",
+            path,
+            name.to_str().unwrap(),
+        )?;
+
+        writeln!(f, "{}", self.doc)?;
+        Ok(())
+    }
+}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index dff3ce4a1dd..9d7cdd1145a 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -75,6 +75,7 @@ FLAGS:
             codegen::generate_syntax(Mode::Overwrite)?;
             codegen::generate_parser_tests(Mode::Overwrite)?;
             codegen::generate_assists_docs(Mode::Overwrite)?;
+            codegen::generate_feature_docs(Mode::Overwrite)?;
             Ok(())
         }
         "format" => {