aboutsummaryrefslogtreecommitdiff
path: root/xtask
diff options
context:
space:
mode:
Diffstat (limited to 'xtask')
-rw-r--r--xtask/src/ast_src.rs2
-rw-r--r--xtask/src/codegen.rs66
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs73
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs23
-rw-r--r--xtask/src/lib.rs6
5 files changed, 100 insertions, 70 deletions
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index d4621930e..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.
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index f47d54125..5511c01d5 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -10,9 +10,12 @@ mod gen_parser_tests;
10mod gen_assists_docs; 10mod gen_assists_docs;
11mod gen_feature_docs; 11mod gen_feature_docs;
12 12
13use std::{mem, path::Path}; 13use std::{
14 fmt, mem,
15 path::{Path, PathBuf},
16};
14 17
15use crate::{not_bash::fs2, Result}; 18use crate::{not_bash::fs2, project_root, Result};
16 19
17pub use self::{ 20pub use self::{
18 gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, 21 gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs,
@@ -29,7 +32,6 @@ const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs";
29 32
30const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers"; 33const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
31const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs"; 34const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs";
32const ASSISTS_DOCS: &str = "docs/user/assists.md";
33 35
34#[derive(Debug, PartialEq, Eq, Clone, Copy)] 36#[derive(Debug, PartialEq, Eq, Clone, Copy)]
35pub enum Mode { 37pub enum Mode {
@@ -59,18 +61,18 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
59} 61}
60 62
61fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { 63fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
62 do_extract_comment_blocks(text, false) 64 do_extract_comment_blocks(text, false).into_iter().map(|(_line, block)| block).collect()
63} 65}
64 66
65fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> { 67fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> {
66 assert!(tag.starts_with(char::is_uppercase)); 68 assert!(tag.starts_with(char::is_uppercase));
67 let tag = format!("{}:", tag); 69 let tag = format!("{}:", tag);
68 let mut res = Vec::new(); 70 let mut res = Vec::new();
69 for mut block in do_extract_comment_blocks(text, true) { 71 for (line, mut block) in do_extract_comment_blocks(text, true) {
70 let first = block.remove(0); 72 let first = block.remove(0);
71 if first.starts_with(&tag) { 73 if first.starts_with(&tag) {
72 let id = first[tag.len()..].trim().to_string(); 74 let id = first[tag.len()..].trim().to_string();
73 let block = CommentBlock { id, contents: block }; 75 let block = CommentBlock { id, line, contents: block };
74 res.push(block); 76 res.push(block);
75 } 77 }
76 } 78 }
@@ -79,31 +81,65 @@ fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<Comment
79 81
80struct CommentBlock { 82struct CommentBlock {
81 id: String, 83 id: String,
84 line: usize,
82 contents: Vec<String>, 85 contents: Vec<String>,
83} 86}
84 87
85fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> { 88fn do_extract_comment_blocks(
89 text: &str,
90 allow_blocks_with_empty_lines: bool,
91) -> Vec<(usize, Vec<String>)> {
86 let mut res = Vec::new(); 92 let mut res = Vec::new();
87 93
88 let prefix = "// "; 94 let prefix = "// ";
89 let lines = text.lines().map(str::trim_start); 95 let lines = text.lines().map(str::trim_start);
90 96
91 let mut block = vec![]; 97 let mut block = (0, vec![]);
92 for line in lines { 98 for (line_num, line) in lines.enumerate() {
93 if line == "//" && allow_blocks_with_empty_lines { 99 if line == "//" && allow_blocks_with_empty_lines {
94 block.push(String::new()); 100 block.1.push(String::new());
95 continue; 101 continue;
96 } 102 }
97 103
98 let is_comment = line.starts_with(prefix); 104 let is_comment = line.starts_with(prefix);
99 if is_comment { 105 if is_comment {
100 block.push(line[prefix.len()..].to_string()); 106 block.1.push(line[prefix.len()..].to_string());
101 } else if !block.is_empty() { 107 } else {
102 res.push(mem::replace(&mut block, Vec::new())); 108 if !block.1.is_empty() {
109 res.push(mem::take(&mut block));
110 }
111 block.0 = line_num + 2;
103 } 112 }
104 } 113 }
105 if !block.is_empty() { 114 if !block.1.is_empty() {
106 res.push(mem::replace(&mut block, Vec::new())) 115 res.push(block)
107 } 116 }
108 res 117 res
109} 118}
119
120#[derive(Debug)]
121struct Location {
122 file: PathBuf,
123 line: usize,
124}
125
126impl Location {
127 fn new(file: PathBuf, line: usize) -> Self {
128 Self { file, line }
129 }
130}
131
132impl fmt::Display for Location {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 let path = self.file.strip_prefix(&project_root()).unwrap().display().to_string();
135 let path = path.replace('\\', "/");
136 let name = self.file.file_name().unwrap();
137 write!(
138 f,
139 "https://github.com/rust-analyzer/rust-analyzer/blob/master/{}#L{}[{}]",
140 path,
141 self.line,
142 name.to_str().unwrap()
143 )
144 }
145}
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 6ebeb8aea..6c1be5350 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -1,22 +1,28 @@
1//! Generates `assists.md` documentation. 1//! Generates `assists.md` documentation.
2 2
3use std::{fs, path::Path}; 3use std::{fmt, fs, path::Path};
4 4
5use crate::{ 5use 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
10pub fn generate_assists_docs(mode: Mode) -> Result<()> { 10pub fn generate_assists_docs(mode: Mode) -> Result<()> {
11 let assists = Assist::collect()?; 11 let assists = Assist::collect()?;
12 generate_tests(&assists, mode)?; 12 generate_tests(&assists, mode)?;
13 generate_docs(&assists, mode)?; 13
14 let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
15 let contents = contents.trim().to_string() + "\n";
16 let dst = project_root().join("docs/user/generated_assists.adoc");
17 codegen::update(&dst, &contents, mode)?;
18
14 Ok(()) 19 Ok(())
15} 20}
16 21
17#[derive(Debug)] 22#[derive(Debug)]
18struct Assist { 23struct Assist {
19 id: String, 24 id: String,
25 location: Location,
20 doc: String, 26 doc: String,
21 before: String, 27 before: String,
22 after: String, 28 after: String,
@@ -58,7 +64,8 @@ impl Assist {
58 assert_eq!(lines.next().unwrap().as_str(), "->"); 64 assert_eq!(lines.next().unwrap().as_str(), "->");
59 assert_eq!(lines.next().unwrap().as_str(), "```"); 65 assert_eq!(lines.next().unwrap().as_str(), "```");
60 let after = take_until(lines.by_ref(), "```"); 66 let after = take_until(lines.by_ref(), "```");
61 acc.push(Assist { id, doc, before, after }) 67 let location = Location::new(path.to_path_buf(), block.line);
68 acc.push(Assist { id, location, doc, before, after })
62 } 69 }
63 70
64 fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String { 71 fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String {
@@ -76,6 +83,33 @@ impl Assist {
76 } 83 }
77} 84}
78 85
86impl fmt::Display for Assist {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 let before = self.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
89 let after = self.after.replace("<|>", "┃");
90 writeln!(
91 f,
92 "[discrete]\n=== `{}`
93**Source:** {}
94
95{}
96
97.Before
98```rust
99{}```
100
101.After
102```rust
103{}```",
104 self.id,
105 self.location,
106 self.doc,
107 hide_hash_comments(&before),
108 hide_hash_comments(&after)
109 )
110 }
111}
112
79fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { 113fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> {
80 let mut buf = String::from("use super::check_doc_test;\n"); 114 let mut buf = String::from("use super::check_doc_test;\n");
81 115
@@ -103,37 +137,6 @@ r#####"
103 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) 137 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode)
104} 138}
105 139
106fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
107 let mut buf = String::from(
108 "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n",
109 );
110
111 for assist in assists {
112 let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
113 let after = assist.after.replace("<|>", "┃");
114 let docs = format!(
115 "
116## `{}`
117
118{}
119
120```rust
121// BEFORE
122{}
123// AFTER
124{}```
125",
126 assist.id,
127 assist.doc,
128 hide_hash_comments(&before),
129 hide_hash_comments(&after)
130 );
131 buf.push_str(&docs);
132 }
133
134 codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
135}
136
137fn hide_hash_comments(text: &str) -> String { 140fn hide_hash_comments(text: &str) -> String {
138 text.split('\n') // want final newline 141 text.split('\n') // want final newline
139 .filter(|&it| !(it.starts_with("# ") || it == "#")) 142 .filter(|&it| !(it.starts_with("# ") || it == "#"))
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index dbe583e8e..31bc3839d 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -3,7 +3,7 @@
3use std::{fmt, fs, path::PathBuf}; 3use std::{fmt, fs, path::PathBuf};
4 4
5use crate::{ 5use 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
@@ -19,7 +19,7 @@ pub fn generate_feature_docs(mode: Mode) -> Result<()> {
19#[derive(Debug)] 19#[derive(Debug)]
20struct Feature { 20struct Feature {
21 id: String, 21 id: String,
22 path: PathBuf, 22 location: Location,
23 doc: String, 23 doc: String,
24} 24}
25 25
@@ -40,7 +40,8 @@ impl Feature {
40 let id = block.id; 40 let id = block.id;
41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); 41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
42 let doc = block.contents.join("\n"); 42 let doc = block.contents.join("\n");
43 acc.push(Feature { id, path: path.clone(), doc }) 43 let location = Location::new(path.clone(), block.line);
44 acc.push(Feature { id, location, doc })
44 } 45 }
45 46
46 Ok(()) 47 Ok(())
@@ -69,20 +70,6 @@ fn is_valid_feature_name(feature: &str) -> bool {
69 70
70impl fmt::Display for Feature { 71impl fmt::Display for Feature {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 writeln!(f, "=== {}", self.id)?; 73 writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
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 } 74 }
88} 75}
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index 2b7a461e5..874957885 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", "generated_assists.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();