aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/expect/src/lib.rs2
-rw-r--r--crates/ra_ide/src/display.rs29
-rw-r--r--crates/ra_ide/src/hover.rs230
-rw-r--r--crates/ra_ide/src/markup.rs16
4 files changed, 114 insertions, 163 deletions
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs
index a5e26fade..7579a5027 100644
--- a/crates/expect/src/lib.rs
+++ b/crates/expect/src/lib.rs
@@ -275,7 +275,7 @@ fn format_patch(line_indent: usize, patch: &str) -> String {
275 } 275 }
276 let mut final_newline = false; 276 let mut final_newline = false;
277 for line in lines_with_ends(patch) { 277 for line in lines_with_ends(patch) {
278 if is_multiline { 278 if is_multiline && !line.trim().is_empty() {
279 buf.push_str(indent); 279 buf.push_str(indent);
280 buf.push_str(" "); 280 buf.push_str(" ");
281 } 281 }
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 827c094e7..70d2a2dd1 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -6,13 +6,10 @@ mod navigation_target;
6mod structure; 6mod structure;
7mod short_label; 7mod short_label;
8 8
9use std::fmt::Display;
10
11use ra_syntax::{ 9use ra_syntax::{
12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 10 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
13 SyntaxKind::{ATTR, COMMENT}, 11 SyntaxKind::{ATTR, COMMENT},
14}; 12};
15use stdx::format_to;
16 13
17pub use function_signature::FunctionSignature; 14pub use function_signature::FunctionSignature;
18pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
@@ -69,29 +66,3 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
69 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; 66 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
70 format!("{}macro_rules! {}", vis, name) 67 format!("{}macro_rules! {}", vis, name)
71} 68}
72
73pub(crate) fn rust_code_markup(code: &impl Display) -> String {
74 rust_code_markup_with_doc(code, None, None)
75}
76
77pub(crate) fn rust_code_markup_with_doc(
78 code: &impl Display,
79 doc: Option<&str>,
80 mod_path: Option<&str>,
81) -> String {
82 let mut buf = String::new();
83
84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() {
86 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
87 }
88 }
89 format_to!(buf, "```rust\n{}\n```", code);
90
91 if let Some(doc) = doc {
92 format_to!(buf, "\n___");
93 format_to!(buf, "\n\n{}", doc);
94 }
95
96 buf
97}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index a18c43003..a4c97e7f9 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,5 +1,3 @@
1use std::iter::once;
2
3use hir::{ 1use hir::{
4 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, 2 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 Module, ModuleDef, ModuleSource, Semantics, 3 Module, ModuleDef, ModuleSource, Semantics,
@@ -11,16 +9,15 @@ use ra_ide_db::{
11 RootDatabase, 9 RootDatabase,
12}; 10};
13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 11use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
12use stdx::format_to;
13use test_utils::mark;
14 14
15use crate::{ 15use crate::{
16 display::{ 16 display::{macro_label, ShortLabel, ToNav, TryToNav},
17 macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav, TryToNav,
18 },
19 markup::Markup, 17 markup::Markup,
20 runnables::runnable, 18 runnables::runnable,
21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 19 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
22}; 20};
23use test_utils::mark;
24 21
25#[derive(Clone, Debug, PartialEq, Eq)] 22#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct HoverConfig { 23pub struct HoverConfig {
@@ -73,20 +70,6 @@ pub struct HoverResult {
73 pub actions: Vec<HoverAction>, 70 pub actions: Vec<HoverAction>,
74} 71}
75 72
76impl HoverResult {
77 pub fn new() -> HoverResult {
78 Self::default()
79 }
80
81 pub fn is_empty(&self) -> bool {
82 self.markup.is_empty()
83 }
84
85 fn push_action(&mut self, action: HoverAction) {
86 self.actions.push(action);
87 }
88}
89
90// Feature: Hover 73// Feature: Hover
91// 74//
92// Shows additional information, like type of an expression or documentation for definition when "focusing" code. 75// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
@@ -97,38 +80,32 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
97 let token = pick_best(file.token_at_offset(position.offset))?; 80 let token = pick_best(file.token_at_offset(position.offset))?;
98 let token = sema.descend_into_macros(token); 81 let token = sema.descend_into_macros(token);
99 82
100 let mut res = HoverResult::new(); 83 let mut res = HoverResult::default();
101 84
102 let node = token.parent(); 85 let node = token.parent();
103 let definition = match_ast! { 86 let definition = match_ast! {
104 match node { 87 match node {
105 ast::NameRef(name_ref) => { 88 ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition()),
106 classify_name_ref(&sema, &name_ref).map(|d| d.definition()) 89 ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition()),
107 },
108 ast::Name(name) => {
109 classify_name(&sema, &name).map(|d| d.definition())
110 },
111 _ => None, 90 _ => None,
112 } 91 }
113 }; 92 };
114 if let Some(definition) = definition { 93 if let Some(definition) = definition {
115 let range = sema.original_range(&node).range; 94 if let Some(markup) = hover_for_definition(db, definition) {
116 if let Some(text) = hover_text_from_name_kind(db, definition) { 95 res.markup = markup;
117 res.markup.push_section(&text);
118 }
119 if !res.is_empty() {
120 if let Some(action) = show_implementations_action(db, definition) { 96 if let Some(action) = show_implementations_action(db, definition) {
121 res.push_action(action); 97 res.actions.push(action);
122 } 98 }
123 99
124 if let Some(action) = runnable_action(&sema, definition, position.file_id) { 100 if let Some(action) = runnable_action(&sema, definition, position.file_id) {
125 res.push_action(action); 101 res.actions.push(action);
126 } 102 }
127 103
128 if let Some(action) = goto_type_action(db, definition) { 104 if let Some(action) = goto_type_action(db, definition) {
129 res.push_action(action); 105 res.actions.push(action);
130 } 106 }
131 107
108 let range = sema.original_range(&node).range;
132 return Some(RangeInfo::new(range, res)); 109 return Some(RangeInfo::new(range, res));
133 } 110 }
134 } 111 }
@@ -139,22 +116,16 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
139 116
140 let ty = match_ast! { 117 let ty = match_ast! {
141 match node { 118 match node {
142 ast::MacroCall(_it) => { 119 ast::Expr(it) => sema.type_of_expr(&it)?,
143 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. 120 ast::Pat(it) => sema.type_of_pat(&it)?,
144 // (e.g expanding a builtin macro). So we give up here. 121 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
145 return None; 122 // (e.g expanding a builtin macro). So we give up here.
146 }, 123 ast::MacroCall(_it) => return None,
147 ast::Expr(it) => { 124 _ => return None,
148 sema.type_of_expr(&it)
149 },
150 ast::Pat(it) => {
151 sema.type_of_pat(&it)
152 },
153 _ => None,
154 } 125 }
155 }?; 126 };
156 127
157 res.markup.push_section(&rust_code_markup(&ty.display(db))); 128 res.markup = Markup::fenced_block(&ty.display(db));
158 let range = sema.original_range(&node).range; 129 let range = sema.original_range(&node).range;
159 Some(RangeInfo::new(range, res)) 130 Some(RangeInfo::new(range, res))
160} 131}
@@ -235,7 +206,11 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
235 .into_iter() 206 .into_iter()
236 .filter_map(|it| { 207 .filter_map(|it| {
237 Some(HoverGotoTypeData { 208 Some(HoverGotoTypeData {
238 mod_path: mod_path(db, &it)?, 209 mod_path: render_path(
210 db,
211 it.module(db)?,
212 it.name(db).map(|name| name.to_string()),
213 ),
239 nav: it.try_to_nav(db)?, 214 nav: it.try_to_nav(db)?,
240 }) 215 })
241 }) 216 })
@@ -247,15 +222,28 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
247 } 222 }
248} 223}
249 224
250fn hover_text( 225fn hover_markup(
251 docs: Option<String>, 226 docs: Option<String>,
252 desc: Option<String>, 227 desc: Option<String>,
253 mod_path: Option<String>, 228 mod_path: Option<String>,
254) -> Option<String> { 229) -> Option<Markup> {
255 if let Some(desc) = desc { 230 match desc {
256 Some(rust_code_markup_with_doc(&desc, docs.as_deref(), mod_path.as_deref())) 231 Some(desc) => {
257 } else { 232 let mut buf = String::new();
258 docs 233
234 if let Some(mod_path) = mod_path {
235 if !mod_path.is_empty() {
236 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
237 }
238 }
239 format_to!(buf, "```rust\n{}\n```", desc);
240
241 if let Some(doc) = docs {
242 format_to!(buf, "\n___\n\n{}", doc);
243 }
244 Some(buf.into())
245 }
246 None => docs.map(Markup::from),
259 } 247 }
260} 248}
261 249
@@ -277,43 +265,35 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
277 .map(|name| name.to_string()) 265 .map(|name| name.to_string())
278} 266}
279 267
280fn determine_mod_path(db: &RootDatabase, module: Module, name: Option<String>) -> String { 268fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
281 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) 269 let crate_name =
282 .chain( 270 db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string);
283 module 271 let module_path = module
284 .path_to_root(db) 272 .path_to_root(db)
285 .into_iter() 273 .into_iter()
286 .rev() 274 .rev()
287 .map(|it| it.name(db).map(|name| name.to_string())), 275 .flat_map(|it| it.name(db).map(|name| name.to_string()));
288 ) 276 crate_name.into_iter().chain(module_path).chain(item_name).join("::")
289 .chain(once(name))
290 .flatten()
291 .join("::")
292}
293
294// returns None only for ModuleDef::BuiltinType
295fn mod_path(db: &RootDatabase, item: &ModuleDef) -> Option<String> {
296 Some(determine_mod_path(db, item.module(db)?, item.name(db).map(|name| name.to_string())))
297} 277}
298 278
299fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 279fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
300 def.module(db).map(|module| determine_mod_path(db, module, definition_owner_name(db, def))) 280 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
301} 281}
302 282
303fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { 283fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
304 let mod_path = definition_mod_path(db, &def); 284 let mod_path = definition_mod_path(db, &def);
305 return match def { 285 return match def {
306 Definition::Macro(it) => { 286 Definition::Macro(it) => {
307 let src = it.source(db); 287 let src = it.source(db);
308 let docs = Documentation::from_ast(&src.value).map(Into::into); 288 let docs = Documentation::from_ast(&src.value).map(Into::into);
309 hover_text(docs, Some(macro_label(&src.value)), mod_path) 289 hover_markup(docs, Some(macro_label(&src.value)), mod_path)
310 } 290 }
311 Definition::Field(it) => { 291 Definition::Field(it) => {
312 let src = it.source(db); 292 let src = it.source(db);
313 match src.value { 293 match src.value {
314 FieldSource::Named(it) => { 294 FieldSource::Named(it) => {
315 let docs = Documentation::from_ast(&it).map(Into::into); 295 let docs = Documentation::from_ast(&it).map(Into::into);
316 hover_text(docs, it.short_label(), mod_path) 296 hover_markup(docs, it.short_label(), mod_path)
317 } 297 }
318 _ => None, 298 _ => None,
319 } 299 }
@@ -322,7 +302,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
322 ModuleDef::Module(it) => match it.definition_source(db).value { 302 ModuleDef::Module(it) => match it.definition_source(db).value {
323 ModuleSource::Module(it) => { 303 ModuleSource::Module(it) => {
324 let docs = Documentation::from_ast(&it).map(Into::into); 304 let docs = Documentation::from_ast(&it).map(Into::into);
325 hover_text(docs, it.short_label(), mod_path) 305 hover_markup(docs, it.short_label(), mod_path)
326 } 306 }
327 _ => None, 307 _ => None,
328 }, 308 },
@@ -335,23 +315,23 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
335 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 315 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
336 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 316 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
337 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 317 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
338 ModuleDef::BuiltinType(it) => Some(it.to_string()), 318 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()),
339 }, 319 },
340 Definition::Local(it) => Some(rust_code_markup(&it.ty(db).display(db))), 320 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))),
341 Definition::TypeParam(_) | Definition::SelfType(_) => { 321 Definition::TypeParam(_) | Definition::SelfType(_) => {
342 // FIXME: Hover for generic param 322 // FIXME: Hover for generic param
343 None 323 None
344 } 324 }
345 }; 325 };
346 326
347 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 327 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
348 where 328 where
349 D: HasSource<Ast = A>, 329 D: HasSource<Ast = A>,
350 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner, 330 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
351 { 331 {
352 let src = def.source(db); 332 let src = def.source(db);
353 let docs = Documentation::from_ast(&src.value).map(Into::into); 333 let docs = Documentation::from_ast(&src.value).map(Into::into);
354 hover_text(docs, src.value.short_label(), mod_path) 334 hover_markup(docs, src.value.short_label(), mod_path)
355 } 335 }
356} 336}
357 337
@@ -388,7 +368,7 @@ mod tests {
388 let content = analysis.db.file_text(position.file_id); 368 let content = analysis.db.file_text(position.file_id);
389 let hovered_element = &content[hover.range]; 369 let hovered_element = &content[hover.range];
390 370
391 let actual = format!("{}:\n{}\n", hovered_element, hover.info.markup); 371 let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
392 expect.assert_eq(&actual) 372 expect.assert_eq(&actual)
393 } 373 }
394 374
@@ -409,7 +389,7 @@ fn main() {
409} 389}
410"#, 390"#,
411 expect![[r#" 391 expect![[r#"
412 foo(): 392 *foo()*
413 ```rust 393 ```rust
414 u32 394 u32
415 ``` 395 ```
@@ -441,7 +421,7 @@ fn main() {
441} 421}
442"#, 422"#,
443 expect![[r#" 423 expect![[r#"
444 iter: 424 *iter*
445 ```rust 425 ```rust
446 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> 426 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
447 ``` 427 ```
@@ -459,7 +439,7 @@ pub fn foo() -> u32 { 1 }
459fn main() { let foo_test = fo<|>o(); } 439fn main() { let foo_test = fo<|>o(); }
460"#, 440"#,
461 expect![[r#" 441 expect![[r#"
462 foo: 442 *foo*
463 ```rust 443 ```rust
464 pub fn foo() -> u32 444 pub fn foo() -> u32
465 ``` 445 ```
@@ -486,7 +466,7 @@ mod c;
486fn main() { let foo_test = fo<|>o(); } 466fn main() { let foo_test = fo<|>o(); }
487 "#, 467 "#,
488 expect![[r#" 468 expect![[r#"
489 foo: 469 *foo*
490 ```rust 470 ```rust
491 {unknown} 471 {unknown}
492 ``` 472 ```
@@ -503,7 +483,7 @@ pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
503fn main() { let foo_test = fo<|>o(); } 483fn main() { let foo_test = fo<|>o(); }
504 "#, 484 "#,
505 expect![[r#" 485 expect![[r#"
506 foo: 486 *foo*
507 ```rust 487 ```rust
508 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str 488 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str
509 ``` 489 ```
@@ -520,7 +500,7 @@ pub fn foo<|>(a: u32, b: u32) -> u32 {}
520fn main() { } 500fn main() { }
521"#, 501"#,
522 expect![[r#" 502 expect![[r#"
523 foo: 503 *foo*
524 ```rust 504 ```rust
525 pub fn foo(a: u32, b: u32) -> u32 505 pub fn foo(a: u32, b: u32) -> u32
526 ``` 506 ```
@@ -540,7 +520,7 @@ fn main() {
540} 520}
541"#, 521"#,
542 expect![[r#" 522 expect![[r#"
543 field_a: 523 *field_a*
544 ```rust 524 ```rust
545 Foo 525 Foo
546 ``` 526 ```
@@ -561,7 +541,7 @@ fn main() {
561} 541}
562"#, 542"#,
563 expect![[r#" 543 expect![[r#"
564 field_a: 544 *field_a*
565 ```rust 545 ```rust
566 Foo 546 Foo
567 ``` 547 ```
@@ -578,7 +558,7 @@ fn main() {
578 check( 558 check(
579 r#"const foo<|>: u32 = 0;"#, 559 r#"const foo<|>: u32 = 0;"#,
580 expect![[r#" 560 expect![[r#"
581 foo: 561 *foo*
582 ```rust 562 ```rust
583 const foo: u32 563 const foo: u32
584 ``` 564 ```
@@ -587,7 +567,7 @@ fn main() {
587 check( 567 check(
588 r#"static foo<|>: u32 = 0;"#, 568 r#"static foo<|>: u32 = 0;"#,
589 expect![[r#" 569 expect![[r#"
590 foo: 570 *foo*
591 ```rust 571 ```rust
592 static foo: u32 572 static foo: u32
593 ``` 573 ```
@@ -605,7 +585,7 @@ fn main() {
605 let zz<|> = Test { t: 23u8, k: 33 }; 585 let zz<|> = Test { t: 23u8, k: 33 };
606}"#, 586}"#,
607 expect![[r#" 587 expect![[r#"
608 zz: 588 *zz*
609 ```rust 589 ```rust
610 Test<i32, u8> 590 Test<i32, u8>
611 ``` 591 ```
@@ -623,7 +603,7 @@ use Option::Some;
623fn main() { So<|>me(12); } 603fn main() { So<|>me(12); }
624"#, 604"#,
625 expect![[r#" 605 expect![[r#"
626 Some: 606 *Some*
627 ```rust 607 ```rust
628 Option 608 Option
629 ``` 609 ```
@@ -642,7 +622,7 @@ use Option::Some;
642fn main() { let b<|>ar = Some(12); } 622fn main() { let b<|>ar = Some(12); }
643"#, 623"#,
644 expect![[r#" 624 expect![[r#"
645 bar: 625 *bar*
646 ```rust 626 ```rust
647 Option<i32> 627 Option<i32>
648 ``` 628 ```
@@ -660,7 +640,7 @@ enum Option<T> {
660} 640}
661"#, 641"#,
662 expect![[r#" 642 expect![[r#"
663 None: 643 *None*
664 ```rust 644 ```rust
665 Option 645 Option
666 ``` 646 ```
@@ -685,7 +665,7 @@ fn main() {
685} 665}
686"#, 666"#,
687 expect![[r#" 667 expect![[r#"
688 Some: 668 *Some*
689 ```rust 669 ```rust
690 Option 670 Option
691 ``` 671 ```
@@ -705,7 +685,7 @@ fn main() {
705 check( 685 check(
706 r#"fn func(foo: i32) { fo<|>o; }"#, 686 r#"fn func(foo: i32) { fo<|>o; }"#,
707 expect![[r#" 687 expect![[r#"
708 foo: 688 *foo*
709 ```rust 689 ```rust
710 i32 690 i32
711 ``` 691 ```
@@ -718,7 +698,7 @@ fn main() {
718 check( 698 check(
719 r#"fn func(fo<|>o: i32) {}"#, 699 r#"fn func(fo<|>o: i32) {}"#,
720 expect![[r#" 700 expect![[r#"
721 foo: 701 *foo*
722 ```rust 702 ```rust
723 i32 703 i32
724 ``` 704 ```
@@ -731,7 +711,7 @@ fn main() {
731 check( 711 check(
732 r#"fn func(foo: i32) { if true { <|>foo; }; }"#, 712 r#"fn func(foo: i32) { if true { <|>foo; }; }"#,
733 expect![[r#" 713 expect![[r#"
734 foo: 714 *foo*
735 ```rust 715 ```rust
736 i32 716 i32
737 ``` 717 ```
@@ -744,7 +724,7 @@ fn main() {
744 check( 724 check(
745 r#"fn func(<|>foo: i32) {}"#, 725 r#"fn func(<|>foo: i32) {}"#,
746 expect![[r#" 726 expect![[r#"
747 foo: 727 *foo*
748 ```rust 728 ```rust
749 i32 729 i32
750 ``` 730 ```
@@ -765,7 +745,7 @@ impl Thing {
765fn main() { let foo_<|>test = Thing::new(); } 745fn main() { let foo_<|>test = Thing::new(); }
766 "#, 746 "#,
767 expect![[r#" 747 expect![[r#"
768 foo_test: 748 *foo_test*
769 ```rust 749 ```rust
770 Thing 750 Thing
771 ``` 751 ```
@@ -788,7 +768,7 @@ mod wrapper {
788fn main() { let foo_test = wrapper::Thing::new<|>(); } 768fn main() { let foo_test = wrapper::Thing::new<|>(); }
789"#, 769"#,
790 expect![[r#" 770 expect![[r#"
791 new: 771 *new*
792 ```rust 772 ```rust
793 wrapper::Thing 773 wrapper::Thing
794 ``` 774 ```
@@ -818,7 +798,7 @@ fn main() {
818} 798}
819"#, 799"#,
820 expect![[r#" 800 expect![[r#"
821 C: 801 *C*
822 ```rust 802 ```rust
823 const C: u32 803 const C: u32
824 ``` 804 ```
@@ -836,7 +816,7 @@ impl Thing {
836} 816}
837"#, 817"#,
838 expect![[r#" 818 expect![[r#"
839 Self { x: 0 }: 819 *Self { x: 0 }*
840 ```rust 820 ```rust
841 Thing 821 Thing
842 ``` 822 ```
@@ -895,7 +875,7 @@ fn y() {
895} 875}
896"#, 876"#,
897 expect![[r#" 877 expect![[r#"
898 x: 878 *x*
899 ```rust 879 ```rust
900 i32 880 i32
901 ``` 881 ```
@@ -912,7 +892,7 @@ macro_rules! foo { () => {} }
912fn f() { fo<|>o!(); } 892fn f() { fo<|>o!(); }
913"#, 893"#,
914 expect![[r#" 894 expect![[r#"
915 foo: 895 *foo*
916 ```rust 896 ```rust
917 macro_rules! foo 897 macro_rules! foo
918 ``` 898 ```
@@ -925,7 +905,7 @@ fn f() { fo<|>o!(); }
925 check( 905 check(
926 r#"struct TS(String, i32<|>);"#, 906 r#"struct TS(String, i32<|>);"#,
927 expect![[r#" 907 expect![[r#"
928 i32: 908 *i32*
929 i32 909 i32
930 "#]], 910 "#]],
931 ) 911 )
@@ -942,7 +922,7 @@ id! {
942} 922}
943"#, 923"#,
944 expect![[r#" 924 expect![[r#"
945 foo: 925 *foo*
946 ```rust 926 ```rust
947 fn foo() 927 fn foo()
948 ``` 928 ```
@@ -958,7 +938,7 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
958fn foo(bar:u32) { let a = id!(ba<|>r); } 938fn foo(bar:u32) { let a = id!(ba<|>r); }
959"#, 939"#,
960 expect![[r#" 940 expect![[r#"
961 bar: 941 *bar*
962 ```rust 942 ```rust
963 u32 943 u32
964 ``` 944 ```
@@ -975,7 +955,7 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
975fn foo(bar:u32) { let a = id!(ba<|>r); } 955fn foo(bar:u32) { let a = id!(ba<|>r); }
976"#, 956"#,
977 expect![[r#" 957 expect![[r#"
978 bar: 958 *bar*
979 ```rust 959 ```rust
980 u32 960 u32
981 ``` 961 ```
@@ -993,7 +973,7 @@ fn bar() -> u32 { 0 }
993fn foo() { let a = id!([0u32, bar(<|>)] ); } 973fn foo() { let a = id!([0u32, bar(<|>)] ); }
994"#, 974"#,
995 expect![[r#" 975 expect![[r#"
996 bar(): 976 *bar()*
997 ```rust 977 ```rust
998 u32 978 u32
999 ``` 979 ```
@@ -1012,7 +992,7 @@ fn foo() {
1012} 992}
1013"#, 993"#,
1014 expect![[r#" 994 expect![[r#"
1015 "Tracks": 995 *"Tracks"*
1016 ```rust 996 ```rust
1017 &str 997 &str
1018 ``` 998 ```
@@ -1033,7 +1013,7 @@ fn foo() {
1033} 1013}
1034"#, 1014"#,
1035 expect![[r#" 1015 expect![[r#"
1036 bar: 1016 *bar*
1037 ```rust 1017 ```rust
1038 fn bar() -> bool 1018 fn bar() -> bool
1039 ``` 1019 ```
@@ -1065,7 +1045,7 @@ fn foo() { }
1065fn bar() { fo<|>o(); } 1045fn bar() { fo<|>o(); }
1066", 1046",
1067 expect![[r#" 1047 expect![[r#"
1068 foo: 1048 *foo*
1069 ```rust 1049 ```rust
1070 fn foo() 1050 fn foo()
1071 ``` 1051 ```
@@ -1081,7 +1061,7 @@ fn bar() { fo<|>o(); }
1081 check( 1061 check(
1082 r#"async fn foo<|>() {}"#, 1062 r#"async fn foo<|>() {}"#,
1083 expect![[r#" 1063 expect![[r#"
1084 foo: 1064 *foo*
1085 ```rust 1065 ```rust
1086 async fn foo() 1066 async fn foo()
1087 ``` 1067 ```
@@ -1090,7 +1070,7 @@ fn bar() { fo<|>o(); }
1090 check( 1070 check(
1091 r#"pub const unsafe fn foo<|>() {}"#, 1071 r#"pub const unsafe fn foo<|>() {}"#,
1092 expect![[r#" 1072 expect![[r#"
1093 foo: 1073 *foo*
1094 ```rust 1074 ```rust
1095 pub const unsafe fn foo() 1075 pub const unsafe fn foo()
1096 ``` 1076 ```
@@ -1099,7 +1079,7 @@ fn bar() { fo<|>o(); }
1099 check( 1079 check(
1100 r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#, 1080 r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#,
1101 expect![[r#" 1081 expect![[r#"
1102 foo: 1082 *foo*
1103 ```rust 1083 ```rust
1104 pub(crate) async unsafe extern "C" fn foo() 1084 pub(crate) async unsafe extern "C" fn foo()
1105 ``` 1085 ```
@@ -1136,7 +1116,7 @@ mod my { pub struct Bar; }
1136fn my() {} 1116fn my() {}
1137"#, 1117"#,
1138 expect![[r#" 1118 expect![[r#"
1139 my: 1119 *my*
1140 ```rust 1120 ```rust
1141 mod my 1121 mod my
1142 ``` 1122 ```
@@ -1154,7 +1134,7 @@ struct Bar;
1154fn foo() { let bar = Ba<|>r; } 1134fn foo() { let bar = Ba<|>r; }
1155"#, 1135"#,
1156 expect![[r#" 1136 expect![[r#"
1157 Bar: 1137 *Bar*
1158 ```rust 1138 ```rust
1159 struct Bar 1139 struct Bar
1160 ``` 1140 ```
@@ -1175,7 +1155,7 @@ struct Bar;
1175fn foo() { let bar = Ba<|>r; } 1155fn foo() { let bar = Ba<|>r; }
1176"#, 1156"#,
1177 expect![[r#" 1157 expect![[r#"
1178 Bar: 1158 *Bar*
1179 ```rust 1159 ```rust
1180 struct Bar 1160 struct Bar
1181 ``` 1161 ```
@@ -1198,7 +1178,7 @@ struct Bar;
1198fn foo() { let bar = Ba<|>r; } 1178fn foo() { let bar = Ba<|>r; }
1199"#, 1179"#,
1200 expect![[r#" 1180 expect![[r#"
1201 Bar: 1181 *Bar*
1202 ```rust 1182 ```rust
1203 struct Bar 1183 struct Bar
1204 ``` 1184 ```
@@ -1234,7 +1214,7 @@ bar!();
1234fn foo() { let bar = Bar; bar.fo<|>o(); } 1214fn foo() { let bar = Bar; bar.fo<|>o(); }
1235"#, 1215"#,
1236 expect![[r#" 1216 expect![[r#"
1237 foo: 1217 *foo*
1238 ```rust 1218 ```rust
1239 Bar 1219 Bar
1240 ``` 1220 ```
@@ -1270,7 +1250,7 @@ bar!();
1270fn foo() { let bar = Bar; bar.fo<|>o(); } 1250fn foo() { let bar = Bar; bar.fo<|>o(); }
1271"#, 1251"#,
1272 expect![[r#" 1252 expect![[r#"
1273 foo: 1253 *foo*
1274 ```rust 1254 ```rust
1275 Bar 1255 Bar
1276 ``` 1256 ```
diff --git a/crates/ra_ide/src/markup.rs b/crates/ra_ide/src/markup.rs
index 2f2b3cc25..60c193c40 100644
--- a/crates/ra_ide/src/markup.rs
+++ b/crates/ra_ide/src/markup.rs
@@ -16,6 +16,12 @@ impl From<Markup> for String {
16 } 16 }
17} 17}
18 18
19impl From<String> for Markup {
20 fn from(text: String) -> Self {
21 Markup { text }
22 }
23}
24
19impl fmt::Display for Markup { 25impl fmt::Display for Markup {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 fmt::Display::fmt(&self.text, f) 27 fmt::Display::fmt(&self.text, f)
@@ -26,13 +32,7 @@ impl Markup {
26 pub fn as_str(&self) -> &str { 32 pub fn as_str(&self) -> &str {
27 self.text.as_str() 33 self.text.as_str()
28 } 34 }
29 pub fn is_empty(&self) -> bool { 35 pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
30 self.text.is_empty() 36 format!("```rust\n{}\n```", contents).into()
31 }
32 pub fn push_section(&mut self, section: &str) {
33 if !self.text.is_empty() {
34 self.text.push_str("\n\n___\n");
35 }
36 self.text.push_str(section);
37 } 37 }
38} 38}