aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/hover.rs113
-rw-r--r--crates/ide/src/lib.rs8
-rw-r--r--crates/ide/src/link_rewrite.rs37
3 files changed, 146 insertions, 12 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 37171cbef..bb9f12cd3 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -14,7 +14,7 @@ use test_utils::mark;
14 14
15use crate::{ 15use crate::{
16 display::{macro_label, ShortLabel, ToNav, TryToNav}, 16 display::{macro_label, ShortLabel, ToNav, TryToNav},
17 link_rewrite::rewrite_links, 17 link_rewrite::{remove_links, rewrite_links},
18 markup::Markup, 18 markup::Markup,
19 runnables::runnable, 19 runnables::runnable,
20 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 20 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
@@ -26,17 +26,29 @@ pub struct HoverConfig {
26 pub run: bool, 26 pub run: bool,
27 pub debug: bool, 27 pub debug: bool,
28 pub goto_type_def: bool, 28 pub goto_type_def: bool,
29 pub links_in_hover: bool,
29} 30}
30 31
31impl Default for HoverConfig { 32impl Default for HoverConfig {
32 fn default() -> Self { 33 fn default() -> Self {
33 Self { implementations: true, run: true, debug: true, goto_type_def: true } 34 Self {
35 implementations: true,
36 run: true,
37 debug: true,
38 goto_type_def: true,
39 links_in_hover: true,
40 }
34 } 41 }
35} 42}
36 43
37impl HoverConfig { 44impl HoverConfig {
38 pub const NO_ACTIONS: Self = 45 pub const NO_ACTIONS: Self = Self {
39 Self { implementations: false, run: false, debug: false, goto_type_def: false }; 46 implementations: false,
47 run: false,
48 debug: false,
49 goto_type_def: false,
50 links_in_hover: true,
51 };
40 52
41 pub fn any(&self) -> bool { 53 pub fn any(&self) -> bool {
42 self.implementations || self.runnable() || self.goto_type_def 54 self.implementations || self.runnable() || self.goto_type_def
@@ -75,7 +87,11 @@ pub struct HoverResult {
75// 87//
76// Shows additional information, like type of an expression or documentation for definition when "focusing" code. 88// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
77// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. 89// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
78pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { 90pub(crate) fn hover(
91 db: &RootDatabase,
92 position: FilePosition,
93 links_in_hover: bool,
94) -> Option<RangeInfo<HoverResult>> {
79 let sema = Semantics::new(db); 95 let sema = Semantics::new(db);
80 let file = sema.parse(position.file_id).syntax().clone(); 96 let file = sema.parse(position.file_id).syntax().clone();
81 let token = pick_best(file.token_at_offset(position.offset))?; 97 let token = pick_best(file.token_at_offset(position.offset))?;
@@ -93,7 +109,11 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
93 }; 109 };
94 if let Some(definition) = definition { 110 if let Some(definition) = definition {
95 if let Some(markup) = hover_for_definition(db, definition) { 111 if let Some(markup) = hover_for_definition(db, definition) {
96 let markup = rewrite_links(db, &markup.as_str(), &definition); 112 let markup = if links_in_hover {
113 rewrite_links(db, &markup.as_str(), &definition)
114 } else {
115 remove_links(&markup.as_str())
116 };
97 res.markup = Markup::from(markup); 117 res.markup = Markup::from(markup);
98 if let Some(action) = show_implementations_action(db, definition) { 118 if let Some(action) = show_implementations_action(db, definition) {
99 res.actions.push(action); 119 res.actions.push(action);
@@ -363,12 +383,23 @@ mod tests {
363 383
364 fn check_hover_no_result(ra_fixture: &str) { 384 fn check_hover_no_result(ra_fixture: &str) {
365 let (analysis, position) = analysis_and_position(ra_fixture); 385 let (analysis, position) = analysis_and_position(ra_fixture);
366 assert!(analysis.hover(position).unwrap().is_none()); 386 assert!(analysis.hover(position, true).unwrap().is_none());
367 } 387 }
368 388
369 fn check(ra_fixture: &str, expect: Expect) { 389 fn check(ra_fixture: &str, expect: Expect) {
370 let (analysis, position) = analysis_and_position(ra_fixture); 390 let (analysis, position) = analysis_and_position(ra_fixture);
371 let hover = analysis.hover(position).unwrap().unwrap(); 391 let hover = analysis.hover(position, true).unwrap().unwrap();
392
393 let content = analysis.db.file_text(position.file_id);
394 let hovered_element = &content[hover.range];
395
396 let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
397 expect.assert_eq(&actual)
398 }
399
400 fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
401 let (analysis, position) = analysis_and_position(ra_fixture);
402 let hover = analysis.hover(position, false).unwrap().unwrap();
372 403
373 let content = analysis.db.file_text(position.file_id); 404 let content = analysis.db.file_text(position.file_id);
374 let hovered_element = &content[hover.range]; 405 let hovered_element = &content[hover.range];
@@ -379,7 +410,7 @@ mod tests {
379 410
380 fn check_actions(ra_fixture: &str, expect: Expect) { 411 fn check_actions(ra_fixture: &str, expect: Expect) {
381 let (analysis, position) = analysis_and_position(ra_fixture); 412 let (analysis, position) = analysis_and_position(ra_fixture);
382 let hover = analysis.hover(position).unwrap().unwrap(); 413 let hover = analysis.hover(position, true).unwrap().unwrap();
383 expect.assert_debug_eq(&hover.info.actions) 414 expect.assert_debug_eq(&hover.info.actions)
384 } 415 }
385 416
@@ -1810,6 +1841,70 @@ struct S {
1810 } 1841 }
1811 1842
1812 #[test] 1843 #[test]
1844 fn test_hover_no_links() {
1845 check_hover_no_links(
1846 r#"
1847/// Test cases:
1848/// case 1. bare URL: https://www.example.com/
1849/// case 2. inline URL with title: [example](https://www.example.com/)
1850/// case 3. code refrence: [`Result`]
1851/// case 4. code refrence but miss footnote: [`String`]
1852/// case 5. autolink: <http://www.example.com/>
1853/// case 6. email address: <[email protected]>
1854/// case 7. refrence: [example][example]
1855/// case 8. collapsed link: [example][]
1856/// case 9. shortcut link: [example]
1857/// case 10. inline without URL: [example]()
1858/// case 11. refrence: [foo][foo]
1859/// case 12. refrence: [foo][bar]
1860/// case 13. collapsed link: [foo][]
1861/// case 14. shortcut link: [foo]
1862/// case 15. inline without URL: [foo]()
1863/// case 16. just escaped text: \[foo]
1864/// case 17. inline link: [Foo](foo::Foo)
1865///
1866/// [`Result`]: ../../std/result/enum.Result.html
1867/// [^example]: https://www.example.com/
1868pub fn fo<|>o() {}
1869"#,
1870 expect![[r#"
1871 *foo*
1872
1873 ```rust
1874 test
1875 ```
1876
1877 ```rust
1878 pub fn foo()
1879 ```
1880
1881 ---
1882
1883 Test cases:
1884 case 1. bare URL: https://www.example.com/
1885 case 2. inline URL with title: [example](https://www.example.com/)
1886 case 3. code refrence: `Result`
1887 case 4. code refrence but miss footnote: `String`
1888 case 5. autolink: http://www.example.com/
1889 case 6. email address: [email protected]
1890 case 7. refrence: example
1891 case 8. collapsed link: example
1892 case 9. shortcut link: example
1893 case 10. inline without URL: example
1894 case 11. refrence: foo
1895 case 12. refrence: foo
1896 case 13. collapsed link: foo
1897 case 14. shortcut link: foo
1898 case 15. inline without URL: foo
1899 case 16. just escaped text: \[foo]
1900 case 17. inline link: Foo
1901
1902 [^example]: https://www.example.com/
1903 "#]],
1904 );
1905 }
1906
1907 #[test]
1813 fn test_hover_macro_generated_struct_fn_doc_comment() { 1908 fn test_hover_macro_generated_struct_fn_doc_comment() {
1814 mark::check!(hover_macro_generated_struct_fn_doc_comment); 1909 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1815 1910
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 3b97e087f..4763c0aac 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -370,8 +370,12 @@ impl Analysis {
370 } 370 }
371 371
372 /// Returns a short text describing element at position. 372 /// Returns a short text describing element at position.
373 pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<HoverResult>>> { 373 pub fn hover(
374 self.with_db(|db| hover::hover(db, position)) 374 &self,
375 position: FilePosition,
376 links_in_hover: bool,
377 ) -> Cancelable<Option<RangeInfo<HoverResult>>> {
378 self.with_db(|db| hover::hover(db, position, links_in_hover))
375 } 379 }
376 380
377 /// Computes parameter information for the given call expression. 381 /// Computes parameter information for the given call expression.
diff --git a/crates/ide/src/link_rewrite.rs b/crates/ide/src/link_rewrite.rs
index acedea71b..107787bb9 100644
--- a/crates/ide/src/link_rewrite.rs
+++ b/crates/ide/src/link_rewrite.rs
@@ -4,7 +4,7 @@
4 4
5use hir::{Adt, Crate, HasAttrs, ModuleDef}; 5use hir::{Adt, Crate, HasAttrs, ModuleDef};
6use ide_db::{defs::Definition, RootDatabase}; 6use ide_db::{defs::Definition, RootDatabase};
7use pulldown_cmark::{CowStr, Event, Options, Parser, Tag}; 7use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag};
8use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; 8use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions};
9use url::Url; 9use url::Url;
10 10
@@ -45,6 +45,41 @@ pub fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition)
45 out 45 out
46} 46}
47 47
48/// Remove all links in markdown documentation.
49pub fn remove_links(markdown: &str) -> String {
50 let mut drop_link = false;
51
52 let mut opts = Options::empty();
53 opts.insert(Options::ENABLE_FOOTNOTES);
54
55 let doc = Parser::new_with_broken_link_callback(
56 markdown,
57 opts,
58 Some(&|_, _| Some((String::new(), String::new()))),
59 );
60 let doc = doc.filter_map(move |evt| match evt {
61 Event::Start(Tag::Link(link_type, ref target, ref title)) => {
62 if link_type == LinkType::Inline && target.contains("://") {
63 Some(Event::Start(Tag::Link(link_type, target.clone(), title.clone())))
64 } else {
65 drop_link = true;
66 None
67 }
68 }
69 Event::End(_) if drop_link => {
70 drop_link = false;
71 None
72 }
73 _ => Some(evt),
74 });
75
76 let mut out = String::new();
77 let mut options = CmarkOptions::default();
78 options.code_block_backticks = 3;
79 cmark_with_options(doc, &mut out, None, options).ok();
80 out
81}
82
48fn rewrite_intra_doc_link( 83fn rewrite_intra_doc_link(
49 db: &RootDatabase, 84 db: &RootDatabase,
50 def: Definition, 85 def: Definition,