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