aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html6
-rw-r--r--crates/ra_ide/src/ssr.rs74
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs14
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs6
4 files changed, 42 insertions, 58 deletions
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
index 5228df267..13a5d1b12 100644
--- a/crates/ra_ide/src/snapshots/highlight_doctest.html
+++ b/crates/ra_ide/src/snapshots/highlight_doctest.html
@@ -73,9 +73,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
73 <span class="comment">///</span> 73 <span class="comment">///</span>
74 <span class="comment">/// ```</span> 74 <span class="comment">/// ```</span>
75 <span class="comment">///</span> 75 <span class="comment">///</span>
76 <span class="comment">/// ```</span> 76 <span class="comment">/// ```rust,no_run</span>
77 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="struct">Foo</span>::<span class="function">new</span>().<span class="function">bar</span>(); 77 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="struct">Foo</span>::<span class="function">new</span>().<span class="function">bar</span>();
78 <span class="comment">/// ```</span> 78 <span class="comment">/// ```</span>
79 <span class="comment">///</span>
80 <span class="comment">/// ```sh</span>
81 <span class="comment">/// echo 1</span>
82 <span class="comment">/// ```</span>
79 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> { 83 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> {
80 <span class="bool_literal">true</span> 84 <span class="bool_literal">true</span>
81 } 85 }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 93e9aee1d..762aab962 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -27,11 +27,11 @@ impl std::error::Error for SsrError {}
27// 27//
28// Search and replace with named wildcards that will match any expression. 28// Search and replace with named wildcards that will match any expression.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. 30// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
31// Available via the command `rust-analyzer.ssr`. 31// Available via the command `rust-analyzer.ssr`.
32// 32//
33// ```rust 33// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] 34// // Using structural search replace command [foo($a, $b) ==>> ($a).foo($b)]
35// 35//
36// // BEFORE 36// // BEFORE
37// String::from(foo(y + 5, z)) 37// String::from(foo(y + 5, z))
@@ -79,7 +79,7 @@ struct SsrPattern {
79 vars: Vec<Var>, 79 vars: Vec<Var>,
80} 80}
81 81
82/// represents an `$var` in an SSR query 82/// Represents a `$var` in an SSR query.
83#[derive(Debug, Clone, PartialEq, Eq, Hash)] 83#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84struct Var(String); 84struct Var(String);
85 85
@@ -122,8 +122,7 @@ impl FromStr for SsrQuery {
122 let mut pattern = it.next().expect("something").to_string(); 122 let mut pattern = it.next().expect("something").to_string();
123 123
124 for part in it.map(split_by_var) { 124 for part in it.map(split_by_var) {
125 let (var, var_type, remainder) = part?; 125 let (var, remainder) = part?;
126 is_expr(var_type)?;
127 let new_var = create_name(var, &mut vars)?; 126 let new_var = create_name(var, &mut vars)?;
128 pattern.push_str(new_var); 127 pattern.push_str(new_var);
129 pattern.push_str(remainder); 128 pattern.push_str(remainder);
@@ -166,15 +165,11 @@ fn traverse(node: &SyntaxNode, go: &mut impl FnMut(&SyntaxNode) -> bool) {
166 } 165 }
167} 166}
168 167
169fn split_by_var(s: &str) -> Result<(&str, &str, &str), SsrError> { 168fn split_by_var(s: &str) -> Result<(&str, &str), SsrError> {
170 let end_of_name = s.find(':').ok_or_else(|| SsrError("Use $<name>:expr".into()))?; 169 let end_of_name = s.find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
171 let name = &s[0..end_of_name]; 170 let name = &s[..end_of_name];
172 is_name(name)?; 171 is_name(name)?;
173 let type_begin = end_of_name + 1; 172 Ok((name, &s[end_of_name..]))
174 let type_length =
175 s[type_begin..].find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
176 let type_name = &s[type_begin..type_begin + type_length];
177 Ok((name, type_name, &s[type_begin + type_length..]))
178} 173}
179 174
180fn is_name(s: &str) -> Result<(), SsrError> { 175fn is_name(s: &str) -> Result<(), SsrError> {
@@ -185,14 +180,6 @@ fn is_name(s: &str) -> Result<(), SsrError> {
185 } 180 }
186} 181}
187 182
188fn is_expr(s: &str) -> Result<(), SsrError> {
189 if s == "expr" {
190 Ok(())
191 } else {
192 Err(SsrError("Only $<name>:expr is supported".into()))
193 }
194}
195
196fn replace_in_template(template: String, var: &str, new_var: &str) -> String { 183fn replace_in_template(template: String, var: &str, new_var: &str) -> String {
197 let name = format!("${}", var); 184 let name = format!("${}", var);
198 template.replace(&name, new_var) 185 template.replace(&name, new_var)
@@ -450,7 +437,7 @@ mod tests {
450 437
451 #[test] 438 #[test]
452 fn parser_happy_case() { 439 fn parser_happy_case() {
453 let result: SsrQuery = "foo($a:expr, $b:expr) ==>> bar($b, $a)".parse().unwrap(); 440 let result: SsrQuery = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap();
454 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)"); 441 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)");
455 assert_eq!(result.pattern.vars.len(), 2); 442 assert_eq!(result.pattern.vars.len(), 2);
456 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); 443 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
@@ -477,30 +464,9 @@ mod tests {
477 } 464 }
478 465
479 #[test] 466 #[test]
480 fn parser_no_pattern_type() {
481 assert_eq!(parse_error_text("foo($a) ==>>"), "Parse error: Use $<name>:expr");
482 }
483
484 #[test]
485 fn parser_invalid_name() {
486 assert_eq!(
487 parse_error_text("foo($a+:expr) ==>>"),
488 "Parse error: Name can contain only alphanumerics and _"
489 );
490 }
491
492 #[test]
493 fn parser_invalid_type() {
494 assert_eq!(
495 parse_error_text("foo($a:ident) ==>>"),
496 "Parse error: Only $<name>:expr is supported"
497 );
498 }
499
500 #[test]
501 fn parser_repeated_name() { 467 fn parser_repeated_name() {
502 assert_eq!( 468 assert_eq!(
503 parse_error_text("foo($a:expr, $a:expr) ==>>"), 469 parse_error_text("foo($a, $a) ==>>"),
504 "Parse error: Name `a` repeats more than once" 470 "Parse error: Name `a` repeats more than once"
505 ); 471 );
506 } 472 }
@@ -517,7 +483,7 @@ mod tests {
517 483
518 #[test] 484 #[test]
519 fn parse_match_replace() { 485 fn parse_match_replace() {
520 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); 486 let query: SsrQuery = "foo($x) ==>> bar($x)".parse().unwrap();
521 let input = "fn main() { foo(1+2); }"; 487 let input = "fn main() { foo(1+2); }";
522 488
523 let code = SourceFile::parse(input).tree(); 489 let code = SourceFile::parse(input).tree();
@@ -549,7 +515,7 @@ mod tests {
549 #[test] 515 #[test]
550 fn ssr_function_to_method() { 516 fn ssr_function_to_method() {
551 assert_ssr_transform( 517 assert_ssr_transform(
552 "my_function($a:expr, $b:expr) ==>> ($a).my_method($b)", 518 "my_function($a, $b) ==>> ($a).my_method($b)",
553 "loop { my_function( other_func(x, y), z + w) }", 519 "loop { my_function( other_func(x, y), z + w) }",
554 "loop { (other_func(x, y)).my_method(z + w) }", 520 "loop { (other_func(x, y)).my_method(z + w) }",
555 ) 521 )
@@ -558,7 +524,7 @@ mod tests {
558 #[test] 524 #[test]
559 fn ssr_nested_function() { 525 fn ssr_nested_function() {
560 assert_ssr_transform( 526 assert_ssr_transform(
561 "foo($a:expr, $b:expr, $c:expr) ==>> bar($c, baz($a, $b))", 527 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))",
562 "fn main { foo (x + value.method(b), x+y-z, true && false) }", 528 "fn main { foo (x + value.method(b), x+y-z, true && false) }",
563 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }", 529 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
564 ) 530 )
@@ -567,7 +533,7 @@ mod tests {
567 #[test] 533 #[test]
568 fn ssr_expected_spacing() { 534 fn ssr_expected_spacing() {
569 assert_ssr_transform( 535 assert_ssr_transform(
570 "foo($x:expr) + bar() ==>> bar($x)", 536 "foo($x) + bar() ==>> bar($x)",
571 "fn main() { foo(5) + bar() }", 537 "fn main() { foo(5) + bar() }",
572 "fn main() { bar(5) }", 538 "fn main() { bar(5) }",
573 ); 539 );
@@ -576,7 +542,7 @@ mod tests {
576 #[test] 542 #[test]
577 fn ssr_with_extra_space() { 543 fn ssr_with_extra_space() {
578 assert_ssr_transform( 544 assert_ssr_transform(
579 "foo($x:expr ) + bar() ==>> bar($x)", 545 "foo($x ) + bar() ==>> bar($x)",
580 "fn main() { foo( 5 ) +bar( ) }", 546 "fn main() { foo( 5 ) +bar( ) }",
581 "fn main() { bar(5) }", 547 "fn main() { bar(5) }",
582 ); 548 );
@@ -585,7 +551,7 @@ mod tests {
585 #[test] 551 #[test]
586 fn ssr_keeps_nested_comment() { 552 fn ssr_keeps_nested_comment() {
587 assert_ssr_transform( 553 assert_ssr_transform(
588 "foo($x:expr) ==>> bar($x)", 554 "foo($x) ==>> bar($x)",
589 "fn main() { foo(other(5 /* using 5 */)) }", 555 "fn main() { foo(other(5 /* using 5 */)) }",
590 "fn main() { bar(other(5 /* using 5 */)) }", 556 "fn main() { bar(other(5 /* using 5 */)) }",
591 ) 557 )
@@ -594,7 +560,7 @@ mod tests {
594 #[test] 560 #[test]
595 fn ssr_keeps_comment() { 561 fn ssr_keeps_comment() {
596 assert_ssr_transform( 562 assert_ssr_transform(
597 "foo($x:expr) ==>> bar($x)", 563 "foo($x) ==>> bar($x)",
598 "fn main() { foo(5 /* using 5 */) }", 564 "fn main() { foo(5 /* using 5 */) }",
599 "fn main() { bar(5)/* using 5 */ }", 565 "fn main() { bar(5)/* using 5 */ }",
600 ) 566 )
@@ -603,7 +569,7 @@ mod tests {
603 #[test] 569 #[test]
604 fn ssr_struct_lit() { 570 fn ssr_struct_lit() {
605 assert_ssr_transform( 571 assert_ssr_transform(
606 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)", 572 "foo{a: $a, b: $b} ==>> foo::new($a, $b)",
607 "fn main() { foo{b:2, a:1} }", 573 "fn main() { foo{b:2, a:1} }",
608 "fn main() { foo::new(1, 2) }", 574 "fn main() { foo::new(1, 2) }",
609 ) 575 )
@@ -612,7 +578,7 @@ mod tests {
612 #[test] 578 #[test]
613 fn ssr_call_and_method_call() { 579 fn ssr_call_and_method_call() {
614 assert_ssr_transform( 580 assert_ssr_transform(
615 "foo::<'a>($a:expr, $b:expr)) ==>> foo2($a, $b)", 581 "foo::<'a>($a, $b)) ==>> foo2($a, $b)",
616 "fn main() { get().bar.foo::<'a>(1); }", 582 "fn main() { get().bar.foo::<'a>(1); }",
617 "fn main() { foo2(get().bar, 1); }", 583 "fn main() { foo2(get().bar, 1); }",
618 ) 584 )
@@ -621,7 +587,7 @@ mod tests {
621 #[test] 587 #[test]
622 fn ssr_method_call_and_call() { 588 fn ssr_method_call_and_call() {
623 assert_ssr_transform( 589 assert_ssr_transform(
624 "$o:expr.foo::<i32>($a:expr)) ==>> $o.foo2($a)", 590 "$o.foo::<i32>($a)) ==>> $o.foo2($a)",
625 "fn main() { X::foo::<i32>(x, 1); }", 591 "fn main() { X::foo::<i32>(x, 1); }",
626 "fn main() { x.foo2(1); }", 592 "fn main() { x.foo2(1); }",
627 ) 593 )
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
index a02ffe59e..929a5cc5c 100644
--- a/crates/ra_ide/src/syntax_highlighting/injection.rs
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -53,6 +53,10 @@ pub(super) fn highlight_injection(
53/// Mapping from extracted documentation code to original code 53/// Mapping from extracted documentation code to original code
54type RangesMap = BTreeMap<TextSize, TextSize>; 54type RangesMap = BTreeMap<TextSize, TextSize>;
55 55
56const RUSTDOC_FENCE: &'static str = "```";
57const RUSTDOC_FENCE_TOKENS: &[&'static str] =
58 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
59
56/// Extracts Rust code from documentation comments as well as a mapping from 60/// Extracts Rust code from documentation comments as well as a mapping from
57/// the extracted source code back to the original source ranges. 61/// the extracted source code back to the original source ranges.
58/// Lastly, a vector of new comment highlight ranges (spanning only the 62/// Lastly, a vector of new comment highlight ranges (spanning only the
@@ -67,6 +71,7 @@ pub(super) fn extract_doc_comments(
67 // Mapping from extracted documentation code to original code 71 // Mapping from extracted documentation code to original code
68 let mut range_mapping: RangesMap = BTreeMap::new(); 72 let mut range_mapping: RangesMap = BTreeMap::new();
69 let mut line_start = TextSize::try_from(prefix.len()).unwrap(); 73 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
74 let mut is_codeblock = false;
70 let mut is_doctest = false; 75 let mut is_doctest = false;
71 // Replace the original, line-spanning comment ranges by new, only comment-prefix 76 // Replace the original, line-spanning comment ranges by new, only comment-prefix
72 // spanning comment ranges. 77 // spanning comment ranges.
@@ -76,8 +81,13 @@ pub(super) fn extract_doc_comments(
76 .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) 81 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
77 .filter(|comment| comment.kind().doc.is_some()) 82 .filter(|comment| comment.kind().doc.is_some())
78 .filter(|comment| { 83 .filter(|comment| {
79 if comment.text().contains("```") { 84 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
80 is_doctest = !is_doctest; 85 is_codeblock = !is_codeblock;
86 // Check whether code is rust by inspecting fence guards
87 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
88 let is_rust =
89 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
90 is_doctest = is_codeblock && is_rust;
81 false 91 false
82 } else { 92 } else {
83 is_doctest 93 is_doctest
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 062b3ff4a..ebf5b50ac 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -329,9 +329,13 @@ impl Foo {
329 /// 329 ///
330 /// ``` 330 /// ```
331 /// 331 ///
332 /// ``` 332 /// ```rust,no_run
333 /// let foobar = Foo::new().bar(); 333 /// let foobar = Foo::new().bar();
334 /// ``` 334 /// ```
335 ///
336 /// ```sh
337 /// echo 1
338 /// ```
335 pub fn foo(&self) -> bool { 339 pub fn foo(&self) -> bool {
336 true 340 true
337 } 341 }