From 383247a9ae8202f20ce6f01d1429c1cd2a11d516 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 00:33:37 +0200 Subject: Generalize --- xtask/src/codegen.rs | 20 ++++++++++++++++++-- xtask/src/codegen/gen_assists_docs.rs | 10 +++------- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'xtask') diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index b4907f4b2..2e8fd3494 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -61,8 +61,24 @@ fn extract_comment_blocks(text: &str) -> Vec> { do_extract_comment_blocks(text, false) } -fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec> { - do_extract_comment_blocks(text, true) +fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec { + 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, } fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec> { diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 20dcde820..6ebeb8aea 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, 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!( -- cgit v1.2.3 From c8f27a4a886413a15a2a6af4a87b39b901c873a8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 01:54:54 +0200 Subject: Generate features docs from source --- xtask/src/codegen.rs | 5 ++- xtask/src/codegen/gen_feature_docs.rs | 72 +++++++++++++++++++++++++++++++++++ xtask/src/main.rs | 1 + xtask/tests/tidy.rs | 7 ++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 xtask/src/codegen/gen_feature_docs.rs (limited to 'xtask') diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 2e8fd3494..3e2a66979 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"; diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs new file mode 100644 index 000000000..583185648 --- /dev/null +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -0,0 +1,72 @@ +//! 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::>().join("\n\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> { + 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, 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!( + id.split_ascii_whitespace().all(|it| it.starts_with(char::is_uppercase)), + "bad feature: {}", + id + ); + let doc = block.contents.join("\n"); + acc.push(Feature { id, path: path.clone(), doc }) + } + + Ok(()) + } + } +} + +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(); + 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.display(), + name.to_str().unwrap(), + )?; + + writeln!(f, "\n{}", self.doc)?; + Ok(()) + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index dff3ce4a1..9d7cdd114 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" => { diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 2e9fcf07c..06ff45d99 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -30,6 +30,13 @@ fn generated_assists_are_fresh() { } } +#[test] +fn generated_features_are_fresh() { + if let Err(error) = codegen::generate_feature_docs(Mode::Verify) { + panic!("{}. Please update features by running `cargo xtask codegen`", error); + } +} + #[test] fn check_code_formatting() { if let Err(error) = run_rustfmt(Mode::Verify) { -- cgit v1.2.3 From 5dd65495699fb3d9c6e4b3c4f27b78a64d23e567 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:28:53 +0200 Subject: Simplify --- xtask/src/codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'xtask') diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 3e2a66979..f47d54125 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -41,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(()); } _ => (), -- cgit v1.2.3 From f593393ebb9bfa515caf168a9f037324eeb6edfe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:45:41 +0200 Subject: Specify actions --- xtask/src/codegen/gen_feature_docs.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'xtask') diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index 583185648..170a3e889 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -38,11 +38,7 @@ impl Feature { for block in comment_blocks { let id = block.id; - assert!( - id.split_ascii_whitespace().all(|it| it.starts_with(char::is_uppercase)), - "bad feature: {}", - 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 }) } @@ -52,6 +48,25 @@ impl Feature { } } +fn is_valid_feature_name(feature: &str) -> bool { + 'word: for word in feature.split_whitespace() { + for &short in ["to"].iter() { + if word == short { + continue 'word; + } + } + for &short in ["To"].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)?; -- cgit v1.2.3 From 8915183d7da07a4b295e5e93a889dea4c15024a0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:59:38 +0200 Subject: Don't require module docs for Features and Assists --- xtask/tests/tidy.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'xtask') diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 06ff45d99..4ac5d929f 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -102,7 +102,7 @@ impl TidyDocs { fn visit(&mut self, path: &Path, text: &str) { // Test hopefully don't really need comments, and for assists we already // have special comments which are source of doc tests and user docs. - if is_exclude_dir(path, &["tests", "test_data", "handlers"]) { + if is_exclude_dir(path, &["tests", "test_data"]) { return; } @@ -117,9 +117,12 @@ impl TidyDocs { if first_line.starts_with("//!") { if first_line.contains("FIXME") { - self.contains_fixme.push(path.to_path_buf()) + self.contains_fixme.push(path.to_path_buf()); } } else { + if text.contains("// Feature:") || text.contains("// Assist:") { + return; + } self.missing_docs.push(path.display().to_string()); } -- cgit v1.2.3 From 1c6a2eb14a84c3a66972d1a6da429cca1aa8b40a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 11:29:19 +0200 Subject: Move the rest of the features to generated docs --- xtask/src/codegen/gen_feature_docs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'xtask') diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index 170a3e889..a0c2ffef9 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -50,12 +50,12 @@ impl Feature { fn is_valid_feature_name(feature: &str) -> bool { 'word: for word in feature.split_whitespace() { - for &short in ["to"].iter() { + for &short in ["to", "and"].iter() { if word == short { continue 'word; } } - for &short in ["To"].iter() { + for &short in ["To", "And"].iter() { if word == short { return false; } -- cgit v1.2.3 From 7e3ee77c83dc4e9af470491046206bf46d2a9a7e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 12:12:41 +0200 Subject: Tweak whitespace --- xtask/src/codegen/gen_feature_docs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'xtask') diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index a0c2ffef9..a6f339e7b 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -10,7 +10,7 @@ use crate::{ pub fn generate_feature_docs(mode: Mode) -> Result<()> { let features = Feature::collect()?; let contents = features.into_iter().map(|it| it.to_string()).collect::>().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(()) @@ -81,7 +81,7 @@ impl fmt::Display for Feature { name.to_str().unwrap(), )?; - writeln!(f, "\n{}", self.doc)?; + writeln!(f, "{}", self.doc)?; Ok(()) } } -- cgit v1.2.3 From 13a996f3b68c175f6e6ad8d89081e45850dc5583 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 12:49:06 +0200 Subject: Force / slashes on windows --- xtask/src/codegen/gen_feature_docs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'xtask') diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index a6f339e7b..dbe583e8e 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -70,14 +70,15 @@ fn is_valid_feature_name(feature: &str) -> bool { 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(); + 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.display(), + path, name.to_str().unwrap(), )?; -- cgit v1.2.3