diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/hover.rs | 113 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/link_rewrite.rs | 37 |
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 | ||
15 | use crate::{ | 15 | use 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 | ||
31 | impl Default for HoverConfig { | 32 | impl 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 | ||
37 | impl HoverConfig { | 44 | impl 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. |
78 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 90 | pub(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/ | ||
1868 | pub 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 | ||
5 | use hir::{Adt, Crate, HasAttrs, ModuleDef}; | 5 | use hir::{Adt, Crate, HasAttrs, ModuleDef}; |
6 | use ide_db::{defs::Definition, RootDatabase}; | 6 | use ide_db::{defs::Definition, RootDatabase}; |
7 | use pulldown_cmark::{CowStr, Event, Options, Parser, Tag}; | 7 | use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag}; |
8 | use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; | 8 | use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; |
9 | use url::Url; | 9 | use 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. | ||
49 | pub 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 | |||
48 | fn rewrite_intra_doc_link( | 83 | fn rewrite_intra_doc_link( |
49 | db: &RootDatabase, | 84 | db: &RootDatabase, |
50 | def: Definition, | 85 | def: Definition, |