diff options
Diffstat (limited to 'xtask/src/codegen')
-rw-r--r-- | xtask/src/codegen/gen_assists_docs.rs | 10 | ||||
-rw-r--r-- | xtask/src/codegen/gen_feature_docs.rs | 88 |
2 files changed, 91 insertions, 7 deletions
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 | } | ||