diff options
Diffstat (limited to 'xtask')
-rw-r--r-- | xtask/src/codegen.rs | 27 | ||||
-rw-r--r-- | xtask/src/codegen/gen_assists_docs.rs | 10 | ||||
-rw-r--r-- | xtask/src/codegen/gen_feature_docs.rs | 88 | ||||
-rw-r--r-- | xtask/src/lib.rs | 6 | ||||
-rw-r--r-- | xtask/src/main.rs | 1 | ||||
-rw-r--r-- | xtask/tests/tidy.rs | 14 |
6 files changed, 131 insertions, 15 deletions
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index b4907f4b2..f47d54125 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -8,14 +8,15 @@ | |||
8 | mod gen_syntax; | 8 | mod gen_syntax; |
9 | mod gen_parser_tests; | 9 | mod gen_parser_tests; |
10 | mod gen_assists_docs; | 10 | mod gen_assists_docs; |
11 | mod gen_feature_docs; | ||
11 | 12 | ||
12 | use std::{mem, path::Path}; | 13 | use std::{mem, path::Path}; |
13 | 14 | ||
14 | use crate::{not_bash::fs2, Result}; | 15 | use crate::{not_bash::fs2, Result}; |
15 | 16 | ||
16 | pub use self::{ | 17 | pub use self::{ |
17 | gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, | 18 | gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, |
18 | gen_syntax::generate_syntax, | 19 | gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, |
19 | }; | 20 | }; |
20 | 21 | ||
21 | const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; | 22 | const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; |
@@ -40,7 +41,7 @@ pub enum Mode { | |||
40 | /// With verify = false, | 41 | /// With verify = false, |
41 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | 42 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { |
42 | match fs2::read_to_string(path) { | 43 | match fs2::read_to_string(path) { |
43 | Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { | 44 | Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { |
44 | return Ok(()); | 45 | return Ok(()); |
45 | } | 46 | } |
46 | _ => (), | 47 | _ => (), |
@@ -61,8 +62,24 @@ fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { | |||
61 | do_extract_comment_blocks(text, false) | 62 | do_extract_comment_blocks(text, false) |
62 | } | 63 | } |
63 | 64 | ||
64 | fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> { | 65 | fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> { |
65 | do_extract_comment_blocks(text, true) | 66 | assert!(tag.starts_with(char::is_uppercase)); |
67 | let tag = format!("{}:", tag); | ||
68 | let mut res = Vec::new(); | ||
69 | for mut block in do_extract_comment_blocks(text, true) { | ||
70 | let first = block.remove(0); | ||
71 | if first.starts_with(&tag) { | ||
72 | let id = first[tag.len()..].trim().to_string(); | ||
73 | let block = CommentBlock { id, contents: block }; | ||
74 | res.push(block); | ||
75 | } | ||
76 | } | ||
77 | res | ||
78 | } | ||
79 | |||
80 | struct CommentBlock { | ||
81 | id: String, | ||
82 | contents: Vec<String>, | ||
66 | } | 83 | } |
67 | 84 | ||
68 | fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> { | 85 | 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 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 { | |||
33 | 33 | ||
34 | fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> { | 34 | fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> { |
35 | let text = fs::read_to_string(path)?; | 35 | let text = fs::read_to_string(path)?; |
36 | let comment_blocks = extract_comment_blocks_with_empty_lines(&text); | 36 | let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text); |
37 | 37 | ||
38 | for block in comment_blocks { | 38 | for block in comment_blocks { |
39 | // FIXME: doesn't support blank lines yet, need to tweak | 39 | // FIXME: doesn't support blank lines yet, need to tweak |
40 | // `extract_comment_blocks` for that. | 40 | // `extract_comment_blocks` for that. |
41 | let mut lines = block.iter(); | 41 | let id = block.id; |
42 | let first_line = lines.next().unwrap(); | ||
43 | if !first_line.starts_with("Assist: ") { | ||
44 | continue; | ||
45 | } | ||
46 | let id = first_line["Assist: ".len()..].to_string(); | ||
47 | assert!( | 42 | assert!( |
48 | id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), | 43 | id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), |
49 | "invalid assist id: {:?}", | 44 | "invalid assist id: {:?}", |
50 | id | 45 | id |
51 | ); | 46 | ); |
47 | let mut lines = block.contents.iter(); | ||
52 | 48 | ||
53 | let doc = take_until(lines.by_ref(), "```").trim().to_string(); | 49 | let doc = take_until(lines.by_ref(), "```").trim().to_string(); |
54 | assert!( | 50 | assert!( |
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs new file mode 100644 index 000000000..dbe583e8e --- /dev/null +++ b/xtask/src/codegen/gen_feature_docs.rs | |||
@@ -0,0 +1,88 @@ | |||
1 | //! Generates `assists.md` documentation. | ||
2 | |||
3 | use std::{fmt, fs, path::PathBuf}; | ||
4 | |||
5 | use crate::{ | ||
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, | ||
7 | project_root, rust_files, Result, | ||
8 | }; | ||
9 | |||
10 | pub fn generate_feature_docs(mode: Mode) -> Result<()> { | ||
11 | let features = Feature::collect()?; | ||
12 | let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | ||
13 | let contents = contents.trim().to_string() + "\n"; | ||
14 | let dst = project_root().join("docs/user/generated_features.adoc"); | ||
15 | codegen::update(&dst, &contents, mode)?; | ||
16 | Ok(()) | ||
17 | } | ||
18 | |||
19 | #[derive(Debug)] | ||
20 | struct Feature { | ||
21 | id: String, | ||
22 | path: PathBuf, | ||
23 | doc: String, | ||
24 | } | ||
25 | |||
26 | impl Feature { | ||
27 | fn collect() -> Result<Vec<Feature>> { | ||
28 | let mut res = Vec::new(); | ||
29 | for path in rust_files(&project_root()) { | ||
30 | collect_file(&mut res, path)?; | ||
31 | } | ||
32 | res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); | ||
33 | return Ok(res); | ||
34 | |||
35 | fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> Result<()> { | ||
36 | let text = fs::read_to_string(&path)?; | ||
37 | let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text); | ||
38 | |||
39 | for block in comment_blocks { | ||
40 | let id = block.id; | ||
41 | assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); | ||
42 | let doc = block.contents.join("\n"); | ||
43 | acc.push(Feature { id, path: path.clone(), doc }) | ||
44 | } | ||
45 | |||
46 | Ok(()) | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | |||
51 | fn is_valid_feature_name(feature: &str) -> bool { | ||
52 | 'word: for word in feature.split_whitespace() { | ||
53 | for &short in ["to", "and"].iter() { | ||
54 | if word == short { | ||
55 | continue 'word; | ||
56 | } | ||
57 | } | ||
58 | for &short in ["To", "And"].iter() { | ||
59 | if word == short { | ||
60 | return false; | ||
61 | } | ||
62 | } | ||
63 | if !word.starts_with(char::is_uppercase) { | ||
64 | return false; | ||
65 | } | ||
66 | } | ||
67 | true | ||
68 | } | ||
69 | |||
70 | impl fmt::Display for Feature { | ||
71 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
72 | writeln!(f, "=== {}", self.id)?; | ||
73 | let path = self.path.strip_prefix(&project_root()).unwrap().display().to_string(); | ||
74 | let path = path.replace('\\', "/"); | ||
75 | let name = self.path.file_name().unwrap(); | ||
76 | |||
77 | //FIXME: generate line number as well | ||
78 | writeln!( | ||
79 | f, | ||
80 | "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]", | ||
81 | path, | ||
82 | name.to_str().unwrap(), | ||
83 | )?; | ||
84 | |||
85 | writeln!(f, "{}", self.doc)?; | ||
86 | Ok(()) | ||
87 | } | ||
88 | } | ||
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 2b7a461e5..06043d19f 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs | |||
@@ -191,7 +191,11 @@ Release: release:{}[] | |||
191 | let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); | 191 | let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); |
192 | fs2::write(&path, &contents)?; | 192 | fs2::write(&path, &contents)?; |
193 | 193 | ||
194 | fs2::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?; | 194 | for &adoc in ["manual.adoc", "generated_features.adoc"].iter() { |
195 | let src = project_root().join("./docs/user/").join(adoc); | ||
196 | let dst = website_root.join(adoc); | ||
197 | fs2::copy(src, dst)?; | ||
198 | } | ||
195 | 199 | ||
196 | let tags = run!("git tag --list"; echo = false)?; | 200 | let tags = run!("git tag --list"; echo = false)?; |
197 | let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); | 201 | let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); |
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: | |||
75 | codegen::generate_syntax(Mode::Overwrite)?; | 75 | codegen::generate_syntax(Mode::Overwrite)?; |
76 | codegen::generate_parser_tests(Mode::Overwrite)?; | 76 | codegen::generate_parser_tests(Mode::Overwrite)?; |
77 | codegen::generate_assists_docs(Mode::Overwrite)?; | 77 | codegen::generate_assists_docs(Mode::Overwrite)?; |
78 | codegen::generate_feature_docs(Mode::Overwrite)?; | ||
78 | Ok(()) | 79 | Ok(()) |
79 | } | 80 | } |
80 | "format" => { | 81 | "format" => { |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 2e9fcf07c..4ac5d929f 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -31,6 +31,13 @@ fn generated_assists_are_fresh() { | |||
31 | } | 31 | } |
32 | 32 | ||
33 | #[test] | 33 | #[test] |
34 | fn generated_features_are_fresh() { | ||
35 | if let Err(error) = codegen::generate_feature_docs(Mode::Verify) { | ||
36 | panic!("{}. Please update features by running `cargo xtask codegen`", error); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | #[test] | ||
34 | fn check_code_formatting() { | 41 | fn check_code_formatting() { |
35 | if let Err(error) = run_rustfmt(Mode::Verify) { | 42 | if let Err(error) = run_rustfmt(Mode::Verify) { |
36 | panic!("{}. Please format the code by running `cargo format`", error); | 43 | panic!("{}. Please format the code by running `cargo format`", error); |
@@ -95,7 +102,7 @@ impl TidyDocs { | |||
95 | fn visit(&mut self, path: &Path, text: &str) { | 102 | fn visit(&mut self, path: &Path, text: &str) { |
96 | // Test hopefully don't really need comments, and for assists we already | 103 | // Test hopefully don't really need comments, and for assists we already |
97 | // have special comments which are source of doc tests and user docs. | 104 | // have special comments which are source of doc tests and user docs. |
98 | if is_exclude_dir(path, &["tests", "test_data", "handlers"]) { | 105 | if is_exclude_dir(path, &["tests", "test_data"]) { |
99 | return; | 106 | return; |
100 | } | 107 | } |
101 | 108 | ||
@@ -110,9 +117,12 @@ impl TidyDocs { | |||
110 | 117 | ||
111 | if first_line.starts_with("//!") { | 118 | if first_line.starts_with("//!") { |
112 | if first_line.contains("FIXME") { | 119 | if first_line.contains("FIXME") { |
113 | self.contains_fixme.push(path.to_path_buf()) | 120 | self.contains_fixme.push(path.to_path_buf()); |
114 | } | 121 | } |
115 | } else { | 122 | } else { |
123 | if text.contains("// Feature:") || text.contains("// Assist:") { | ||
124 | return; | ||
125 | } | ||
116 | self.missing_docs.push(path.display().to_string()); | 126 | self.missing_docs.push(path.display().to_string()); |
117 | } | 127 | } |
118 | 128 | ||