diff options
author | Mikhail Rakhmanov <[email protected]> | 2020-06-03 19:10:54 +0100 |
---|---|---|
committer | Mikhail Rakhmanov <[email protected]> | 2020-06-03 19:10:54 +0100 |
commit | eefa10bc6bff3624ddd0bbb6bc89d8beb4bed186 (patch) | |
tree | 15c38c2993c52f4065d338090ca9185cc1fcd3da /xtask | |
parent | a9d567584857b1be4ca8eaa5ef2c7d85f7b2845e (diff) | |
parent | 794f6da821c5d6e2490b996baffe162e4753262d (diff) |
Merge branch 'master' into assists_extract_enum
Diffstat (limited to 'xtask')
-rw-r--r-- | xtask/src/ast_src.rs | 6 | ||||
-rw-r--r-- | xtask/src/codegen.rs | 89 | ||||
-rw-r--r-- | xtask/src/codegen/gen_assists_docs.rs | 210 | ||||
-rw-r--r-- | xtask/src/codegen/gen_feature_docs.rs | 75 | ||||
-rw-r--r-- | xtask/src/lib.rs | 8 | ||||
-rw-r--r-- | xtask/src/main.rs | 2 | ||||
-rw-r--r-- | xtask/tests/tidy.rs | 25 |
7 files changed, 279 insertions, 136 deletions
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index 394a7bc88..f60f0fb16 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs | |||
@@ -1058,7 +1058,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { | |||
1058 | /// [Reference](https://doc.rust-lang.org/reference/expressions/block-expr.html) | 1058 | /// [Reference](https://doc.rust-lang.org/reference/expressions/block-expr.html) |
1059 | /// [Labels for blocks RFC](https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md) | 1059 | /// [Labels for blocks RFC](https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md) |
1060 | struct BlockExpr: AttrsOwner, ModuleItemOwner { | 1060 | struct BlockExpr: AttrsOwner, ModuleItemOwner { |
1061 | T!['{'], statements: [Stmt], Expr, T!['}'], | 1061 | Label, T!['{'], statements: [Stmt], Expr, T!['}'], |
1062 | } | 1062 | } |
1063 | 1063 | ||
1064 | /// Return expression. | 1064 | /// Return expression. |
@@ -1153,10 +1153,12 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { | |||
1153 | /// ``` | 1153 | /// ``` |
1154 | /// ❰ &foo ❱; | 1154 | /// ❰ &foo ❱; |
1155 | /// ❰ &mut bar ❱; | 1155 | /// ❰ &mut bar ❱; |
1156 | /// ❰ &raw const bar ❱; | ||
1157 | /// ❰ &raw mut bar ❱; | ||
1156 | /// ``` | 1158 | /// ``` |
1157 | /// | 1159 | /// |
1158 | /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators) | 1160 | /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators) |
1159 | struct RefExpr: AttrsOwner { T![&], T![raw], T![mut], Expr } | 1161 | struct RefExpr: AttrsOwner { T![&], T![raw], T![mut], T![const], Expr } |
1160 | 1162 | ||
1161 | /// Prefix operator call. This is either `!` or `*` or `-`. | 1163 | /// Prefix operator call. This is either `!` or `*` or `-`. |
1162 | /// | 1164 | /// |
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index b4907f4b2..f5f4b964a 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -8,13 +8,19 @@ | |||
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::{ |
14 | fmt, mem, | ||
15 | path::{Path, PathBuf}, | ||
16 | }; | ||
13 | 17 | ||
14 | use crate::{not_bash::fs2, Result}; | 18 | use crate::{not_bash::fs2, project_root, Result}; |
15 | 19 | ||
16 | pub use self::{ | 20 | pub use self::{ |
17 | gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, | 21 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, |
22 | gen_feature_docs::generate_feature_docs, | ||
23 | gen_parser_tests::generate_parser_tests, | ||
18 | gen_syntax::generate_syntax, | 24 | gen_syntax::generate_syntax, |
19 | }; | 25 | }; |
20 | 26 | ||
@@ -28,7 +34,6 @@ const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs"; | |||
28 | 34 | ||
29 | const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers"; | 35 | const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers"; |
30 | const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs"; | 36 | const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs"; |
31 | const ASSISTS_DOCS: &str = "docs/user/assists.md"; | ||
32 | 37 | ||
33 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 38 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
34 | pub enum Mode { | 39 | pub enum Mode { |
@@ -40,7 +45,7 @@ pub enum Mode { | |||
40 | /// With verify = false, | 45 | /// With verify = false, |
41 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | 46 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { |
42 | match fs2::read_to_string(path) { | 47 | match fs2::read_to_string(path) { |
43 | Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { | 48 | Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { |
44 | return Ok(()); | 49 | return Ok(()); |
45 | } | 50 | } |
46 | _ => (), | 51 | _ => (), |
@@ -58,35 +63,85 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | |||
58 | } | 63 | } |
59 | 64 | ||
60 | fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { | 65 | fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { |
61 | do_extract_comment_blocks(text, false) | 66 | do_extract_comment_blocks(text, false).into_iter().map(|(_line, block)| block).collect() |
67 | } | ||
68 | |||
69 | fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> { | ||
70 | assert!(tag.starts_with(char::is_uppercase)); | ||
71 | let tag = format!("{}:", tag); | ||
72 | let mut res = Vec::new(); | ||
73 | for (line, mut block) in do_extract_comment_blocks(text, true) { | ||
74 | let first = block.remove(0); | ||
75 | if first.starts_with(&tag) { | ||
76 | let id = first[tag.len()..].trim().to_string(); | ||
77 | let block = CommentBlock { id, line, contents: block }; | ||
78 | res.push(block); | ||
79 | } | ||
80 | } | ||
81 | res | ||
62 | } | 82 | } |
63 | 83 | ||
64 | fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> { | 84 | struct CommentBlock { |
65 | do_extract_comment_blocks(text, true) | 85 | id: String, |
86 | line: usize, | ||
87 | contents: Vec<String>, | ||
66 | } | 88 | } |
67 | 89 | ||
68 | fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> { | 90 | fn do_extract_comment_blocks( |
91 | text: &str, | ||
92 | allow_blocks_with_empty_lines: bool, | ||
93 | ) -> Vec<(usize, Vec<String>)> { | ||
69 | let mut res = Vec::new(); | 94 | let mut res = Vec::new(); |
70 | 95 | ||
71 | let prefix = "// "; | 96 | let prefix = "// "; |
72 | let lines = text.lines().map(str::trim_start); | 97 | let lines = text.lines().map(str::trim_start); |
73 | 98 | ||
74 | let mut block = vec![]; | 99 | let mut block = (0, vec![]); |
75 | for line in lines { | 100 | for (line_num, line) in lines.enumerate() { |
76 | if line == "//" && allow_blocks_with_empty_lines { | 101 | if line == "//" && allow_blocks_with_empty_lines { |
77 | block.push(String::new()); | 102 | block.1.push(String::new()); |
78 | continue; | 103 | continue; |
79 | } | 104 | } |
80 | 105 | ||
81 | let is_comment = line.starts_with(prefix); | 106 | let is_comment = line.starts_with(prefix); |
82 | if is_comment { | 107 | if is_comment { |
83 | block.push(line[prefix.len()..].to_string()); | 108 | block.1.push(line[prefix.len()..].to_string()); |
84 | } else if !block.is_empty() { | 109 | } else { |
85 | res.push(mem::replace(&mut block, Vec::new())); | 110 | if !block.1.is_empty() { |
111 | res.push(mem::take(&mut block)); | ||
112 | } | ||
113 | block.0 = line_num + 2; | ||
86 | } | 114 | } |
87 | } | 115 | } |
88 | if !block.is_empty() { | 116 | if !block.1.is_empty() { |
89 | res.push(mem::replace(&mut block, Vec::new())) | 117 | res.push(block) |
90 | } | 118 | } |
91 | res | 119 | res |
92 | } | 120 | } |
121 | |||
122 | #[derive(Debug)] | ||
123 | struct Location { | ||
124 | file: PathBuf, | ||
125 | line: usize, | ||
126 | } | ||
127 | |||
128 | impl Location { | ||
129 | fn new(file: PathBuf, line: usize) -> Self { | ||
130 | Self { file, line } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | impl fmt::Display for Location { | ||
135 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
136 | let path = self.file.strip_prefix(&project_root()).unwrap().display().to_string(); | ||
137 | let path = path.replace('\\', "/"); | ||
138 | let name = self.file.file_name().unwrap(); | ||
139 | write!( | ||
140 | f, | ||
141 | "https://github.com/rust-analyzer/rust-analyzer/blob/master/{}#L{}[{}]", | ||
142 | path, | ||
143 | self.line, | ||
144 | name.to_str().unwrap() | ||
145 | ) | ||
146 | } | ||
147 | } | ||
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 4bd6b5f0c..526941f73 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs | |||
@@ -1,102 +1,113 @@ | |||
1 | //! Generates `assists.md` documentation. | 1 | //! Generates `assists.md` documentation. |
2 | 2 | ||
3 | use std::{fs, path::Path}; | 3 | use std::{fmt, fs, path::Path}; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, | 6 | codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode}, |
7 | project_root, rust_files, Result, | 7 | project_root, rust_files, Result, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | pub fn generate_assists_tests(mode: Mode) -> Result<()> { | ||
11 | let assists = Assist::collect()?; | ||
12 | generate_tests(&assists, mode) | ||
13 | } | ||
14 | |||
10 | pub fn generate_assists_docs(mode: Mode) -> Result<()> { | 15 | pub fn generate_assists_docs(mode: Mode) -> Result<()> { |
11 | let assists = collect_assists()?; | 16 | let assists = Assist::collect()?; |
12 | generate_tests(&assists, mode)?; | 17 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
13 | generate_docs(&assists, mode)?; | 18 | let contents = contents.trim().to_string() + "\n"; |
14 | Ok(()) | 19 | let dst = project_root().join("docs/user/generated_assists.adoc"); |
20 | codegen::update(&dst, &contents, mode) | ||
15 | } | 21 | } |
16 | 22 | ||
17 | #[derive(Debug)] | 23 | #[derive(Debug)] |
18 | struct Assist { | 24 | struct Assist { |
19 | id: String, | 25 | id: String, |
26 | location: Location, | ||
20 | doc: String, | 27 | doc: String, |
21 | before: String, | 28 | before: String, |
22 | after: String, | 29 | after: String, |
23 | } | 30 | } |
24 | 31 | ||
25 | fn hide_hash_comments(text: &str) -> String { | 32 | impl Assist { |
26 | text.split('\n') // want final newline | 33 | fn collect() -> Result<Vec<Assist>> { |
27 | .filter(|&it| !(it.starts_with("# ") || it == "#")) | 34 | let mut res = Vec::new(); |
28 | .map(|it| format!("{}\n", it)) | 35 | for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) { |
29 | .collect() | 36 | collect_file(&mut res, path.as_path())?; |
30 | } | ||
31 | |||
32 | fn reveal_hash_comments(text: &str) -> String { | ||
33 | text.split('\n') // want final newline | ||
34 | .map(|it| { | ||
35 | if it.starts_with("# ") { | ||
36 | &it[2..] | ||
37 | } else if it == "#" { | ||
38 | "" | ||
39 | } else { | ||
40 | it | ||
41 | } | ||
42 | }) | ||
43 | .map(|it| format!("{}\n", it)) | ||
44 | .collect() | ||
45 | } | ||
46 | |||
47 | fn collect_assists() -> Result<Vec<Assist>> { | ||
48 | let mut res = Vec::new(); | ||
49 | for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) { | ||
50 | collect_file(&mut res, path.as_path())?; | ||
51 | } | ||
52 | res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); | ||
53 | return Ok(res); | ||
54 | |||
55 | fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> { | ||
56 | let text = fs::read_to_string(path)?; | ||
57 | let comment_blocks = extract_comment_blocks_with_empty_lines(&text); | ||
58 | |||
59 | for block in comment_blocks { | ||
60 | // FIXME: doesn't support blank lines yet, need to tweak | ||
61 | // `extract_comment_blocks` for that. | ||
62 | let mut lines = block.iter(); | ||
63 | let first_line = lines.next().unwrap(); | ||
64 | if !first_line.starts_with("Assist: ") { | ||
65 | continue; | ||
66 | } | ||
67 | let id = first_line["Assist: ".len()..].to_string(); | ||
68 | assert!( | ||
69 | id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), | ||
70 | "invalid assist id: {:?}", | ||
71 | id | ||
72 | ); | ||
73 | |||
74 | let doc = take_until(lines.by_ref(), "```").trim().to_string(); | ||
75 | assert!( | ||
76 | doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'), | ||
77 | "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n", | ||
78 | id, doc, | ||
79 | ); | ||
80 | |||
81 | let before = take_until(lines.by_ref(), "```"); | ||
82 | |||
83 | assert_eq!(lines.next().unwrap().as_str(), "->"); | ||
84 | assert_eq!(lines.next().unwrap().as_str(), "```"); | ||
85 | let after = take_until(lines.by_ref(), "```"); | ||
86 | acc.push(Assist { id, doc, before, after }) | ||
87 | } | 37 | } |
38 | res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); | ||
39 | return Ok(res); | ||
40 | |||
41 | fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> { | ||
42 | let text = fs::read_to_string(path)?; | ||
43 | let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text); | ||
44 | |||
45 | for block in comment_blocks { | ||
46 | // FIXME: doesn't support blank lines yet, need to tweak | ||
47 | // `extract_comment_blocks` for that. | ||
48 | let id = block.id; | ||
49 | assert!( | ||
50 | id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), | ||
51 | "invalid assist id: {:?}", | ||
52 | id | ||
53 | ); | ||
54 | let mut lines = block.contents.iter(); | ||
55 | |||
56 | let doc = take_until(lines.by_ref(), "```").trim().to_string(); | ||
57 | assert!( | ||
58 | doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'), | ||
59 | "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n", | ||
60 | id, doc, | ||
61 | ); | ||
62 | |||
63 | let before = take_until(lines.by_ref(), "```"); | ||
64 | |||
65 | assert_eq!(lines.next().unwrap().as_str(), "->"); | ||
66 | assert_eq!(lines.next().unwrap().as_str(), "```"); | ||
67 | let after = take_until(lines.by_ref(), "```"); | ||
68 | let location = Location::new(path.to_path_buf(), block.line); | ||
69 | acc.push(Assist { id, location, doc, before, after }) | ||
70 | } | ||
88 | 71 | ||
89 | fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String { | 72 | fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String { |
90 | let mut buf = Vec::new(); | 73 | let mut buf = Vec::new(); |
91 | for line in lines { | 74 | for line in lines { |
92 | if line == marker { | 75 | if line == marker { |
93 | break; | 76 | break; |
77 | } | ||
78 | buf.push(line.clone()); | ||
94 | } | 79 | } |
95 | buf.push(line.clone()); | 80 | buf.join("\n") |
96 | } | 81 | } |
97 | buf.join("\n") | 82 | Ok(()) |
98 | } | 83 | } |
99 | Ok(()) | 84 | } |
85 | } | ||
86 | |||
87 | impl fmt::Display for Assist { | ||
88 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
89 | let before = self.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar | ||
90 | let after = self.after.replace("<|>", "┃"); | ||
91 | writeln!( | ||
92 | f, | ||
93 | "[discrete]\n=== `{}` | ||
94 | **Source:** {} | ||
95 | |||
96 | {} | ||
97 | |||
98 | .Before | ||
99 | ```rust | ||
100 | {}``` | ||
101 | |||
102 | .After | ||
103 | ```rust | ||
104 | {}```", | ||
105 | self.id, | ||
106 | self.location, | ||
107 | self.doc, | ||
108 | hide_hash_comments(&before), | ||
109 | hide_hash_comments(&after) | ||
110 | ) | ||
100 | } | 111 | } |
101 | } | 112 | } |
102 | 113 | ||
@@ -127,33 +138,24 @@ r#####" | |||
127 | codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) | 138 | codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) |
128 | } | 139 | } |
129 | 140 | ||
130 | fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> { | 141 | fn hide_hash_comments(text: &str) -> String { |
131 | let mut buf = String::from( | 142 | text.split('\n') // want final newline |
132 | "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n", | 143 | .filter(|&it| !(it.starts_with("# ") || it == "#")) |
133 | ); | 144 | .map(|it| format!("{}\n", it)) |
134 | 145 | .collect() | |
135 | for assist in assists { | 146 | } |
136 | let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar | ||
137 | let after = assist.after.replace("<|>", "┃"); | ||
138 | let docs = format!( | ||
139 | " | ||
140 | ## `{}` | ||
141 | |||
142 | {} | ||
143 | |||
144 | ```rust | ||
145 | // BEFORE | ||
146 | {} | ||
147 | // AFTER | ||
148 | {}``` | ||
149 | ", | ||
150 | assist.id, | ||
151 | assist.doc, | ||
152 | hide_hash_comments(&before), | ||
153 | hide_hash_comments(&after) | ||
154 | ); | ||
155 | buf.push_str(&docs); | ||
156 | } | ||
157 | 147 | ||
158 | codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode) | 148 | fn reveal_hash_comments(text: &str) -> String { |
149 | text.split('\n') // want final newline | ||
150 | .map(|it| { | ||
151 | if it.starts_with("# ") { | ||
152 | &it[2..] | ||
153 | } else if it == "#" { | ||
154 | "" | ||
155 | } else { | ||
156 | it | ||
157 | } | ||
158 | }) | ||
159 | .map(|it| format!("{}\n", it)) | ||
160 | .collect() | ||
159 | } | 161 | } |
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs new file mode 100644 index 000000000..31bc3839d --- /dev/null +++ b/xtask/src/codegen/gen_feature_docs.rs | |||
@@ -0,0 +1,75 @@ | |||
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, Location, 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 | location: Location, | ||
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 | let location = Location::new(path.clone(), block.line); | ||
44 | acc.push(Feature { id, location, doc }) | ||
45 | } | ||
46 | |||
47 | Ok(()) | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | fn is_valid_feature_name(feature: &str) -> bool { | ||
53 | 'word: for word in feature.split_whitespace() { | ||
54 | for &short in ["to", "and"].iter() { | ||
55 | if word == short { | ||
56 | continue 'word; | ||
57 | } | ||
58 | } | ||
59 | for &short in ["To", "And"].iter() { | ||
60 | if word == short { | ||
61 | return false; | ||
62 | } | ||
63 | } | ||
64 | if !word.starts_with(char::is_uppercase) { | ||
65 | return false; | ||
66 | } | ||
67 | } | ||
68 | true | ||
69 | } | ||
70 | |||
71 | impl fmt::Display for Feature { | ||
72 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
73 | writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) | ||
74 | } | ||
75 | } | ||
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 2b7a461e5..739f49f7b 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs | |||
@@ -160,6 +160,8 @@ pub fn run_release(dry_run: bool) -> Result<()> { | |||
160 | run!("git reset --hard tags/nightly")?; | 160 | run!("git reset --hard tags/nightly")?; |
161 | run!("git push")?; | 161 | run!("git push")?; |
162 | } | 162 | } |
163 | codegen::generate_assists_docs(Mode::Overwrite)?; | ||
164 | codegen::generate_feature_docs(Mode::Overwrite)?; | ||
163 | 165 | ||
164 | let website_root = project_root().join("../rust-analyzer.github.io"); | 166 | let website_root = project_root().join("../rust-analyzer.github.io"); |
165 | let changelog_dir = website_root.join("./thisweek/_posts"); | 167 | let changelog_dir = website_root.join("./thisweek/_posts"); |
@@ -191,7 +193,11 @@ Release: release:{}[] | |||
191 | let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); | 193 | let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); |
192 | fs2::write(&path, &contents)?; | 194 | fs2::write(&path, &contents)?; |
193 | 195 | ||
194 | fs2::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?; | 196 | for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { |
197 | let src = project_root().join("./docs/user/").join(adoc); | ||
198 | let dst = website_root.join(adoc); | ||
199 | fs2::copy(src, dst)?; | ||
200 | } | ||
195 | 201 | ||
196 | let tags = run!("git tag --list"; echo = false)?; | 202 | let tags = run!("git tag --list"; echo = false)?; |
197 | let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); | 203 | 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..81bb3a33f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -74,7 +74,9 @@ FLAGS: | |||
74 | args.finish()?; | 74 | args.finish()?; |
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_tests(Mode::Overwrite)?; | ||
77 | codegen::generate_assists_docs(Mode::Overwrite)?; | 78 | codegen::generate_assists_docs(Mode::Overwrite)?; |
79 | codegen::generate_feature_docs(Mode::Overwrite)?; | ||
78 | Ok(()) | 80 | Ok(()) |
79 | } | 81 | } |
80 | "format" => { | 82 | "format" => { |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 2e9fcf07c..d38ac7f17 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -25,7 +25,7 @@ fn generated_tests_are_fresh() { | |||
25 | 25 | ||
26 | #[test] | 26 | #[test] |
27 | fn generated_assists_are_fresh() { | 27 | fn generated_assists_are_fresh() { |
28 | if let Err(error) = codegen::generate_assists_docs(Mode::Verify) { | 28 | if let Err(error) = codegen::generate_assists_tests(Mode::Verify) { |
29 | panic!("{}. Please update assists by running `cargo xtask codegen`", error); | 29 | panic!("{}. Please update assists by running `cargo xtask codegen`", error); |
30 | } | 30 | } |
31 | } | 31 | } |
@@ -95,7 +95,7 @@ impl TidyDocs { | |||
95 | fn visit(&mut self, path: &Path, text: &str) { | 95 | fn visit(&mut self, path: &Path, text: &str) { |
96 | // Test hopefully don't really need comments, and for assists we already | 96 | // 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. | 97 | // have special comments which are source of doc tests and user docs. |
98 | if is_exclude_dir(path, &["tests", "test_data", "handlers"]) { | 98 | if is_exclude_dir(path, &["tests", "test_data"]) { |
99 | return; | 99 | return; |
100 | } | 100 | } |
101 | 101 | ||
@@ -110,9 +110,12 @@ impl TidyDocs { | |||
110 | 110 | ||
111 | if first_line.starts_with("//!") { | 111 | if first_line.starts_with("//!") { |
112 | if first_line.contains("FIXME") { | 112 | if first_line.contains("FIXME") { |
113 | self.contains_fixme.push(path.to_path_buf()) | 113 | self.contains_fixme.push(path.to_path_buf()); |
114 | } | 114 | } |
115 | } else { | 115 | } else { |
116 | if text.contains("// Feature:") || text.contains("// Assist:") { | ||
117 | return; | ||
118 | } | ||
116 | self.missing_docs.push(path.display().to_string()); | 119 | self.missing_docs.push(path.display().to_string()); |
117 | } | 120 | } |
118 | 121 | ||
@@ -170,13 +173,11 @@ impl TidyDocs { | |||
170 | } | 173 | } |
171 | 174 | ||
172 | fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { | 175 | fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { |
173 | let mut cur_path = p; | 176 | p.strip_prefix(project_root()) |
174 | while let Some(path) = cur_path.parent() { | 177 | .unwrap() |
175 | if dirs_to_exclude.iter().any(|dir| path.ends_with(dir)) { | 178 | .components() |
176 | return true; | 179 | .rev() |
177 | } | 180 | .skip(1) |
178 | cur_path = path; | 181 | .filter_map(|it| it.as_os_str().to_str()) |
179 | } | 182 | .any(|it| dirs_to_exclude.contains(&it)) |
180 | |||
181 | false | ||
182 | } | 183 | } |