From e73ee9dfa28e2c093cc79e0e8d729945c43f3c81 Mon Sep 17 00:00:00 2001 From: flw Date: Sat, 26 Sep 2020 13:02:09 +0800 Subject: Add hover config `linksInHover` to suppress links --- crates/ide/src/hover.rs | 113 +++++++++++++++++++++++++++++++++++++---- crates/ide/src/lib.rs | 8 ++- crates/ide/src/link_rewrite.rs | 37 +++++++++++++- 3 files changed, 146 insertions(+), 12 deletions(-) (limited to 'crates/ide/src') 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; use crate::{ display::{macro_label, ShortLabel, ToNav, TryToNav}, - link_rewrite::rewrite_links, + link_rewrite::{remove_links, rewrite_links}, markup::Markup, runnables::runnable, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, @@ -26,17 +26,29 @@ pub struct HoverConfig { pub run: bool, pub debug: bool, pub goto_type_def: bool, + pub links_in_hover: bool, } impl Default for HoverConfig { fn default() -> Self { - Self { implementations: true, run: true, debug: true, goto_type_def: true } + Self { + implementations: true, + run: true, + debug: true, + goto_type_def: true, + links_in_hover: true, + } } } impl HoverConfig { - pub const NO_ACTIONS: Self = - Self { implementations: false, run: false, debug: false, goto_type_def: false }; + pub const NO_ACTIONS: Self = Self { + implementations: false, + run: false, + debug: false, + goto_type_def: false, + links_in_hover: true, + }; pub fn any(&self) -> bool { self.implementations || self.runnable() || self.goto_type_def @@ -75,7 +87,11 @@ pub struct HoverResult { // // Shows additional information, like type of an expression or documentation for definition when "focusing" code. // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. -pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> { +pub(crate) fn hover( + db: &RootDatabase, + position: FilePosition, + links_in_hover: bool, +) -> Option> { let sema = Semantics::new(db); let file = sema.parse(position.file_id).syntax().clone(); let token = pick_best(file.token_at_offset(position.offset))?; @@ -93,7 +109,11 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option +/// case 6. email address: +/// case 7. refrence: [example][example] +/// case 8. collapsed link: [example][] +/// case 9. shortcut link: [example] +/// case 10. inline without URL: [example]() +/// case 11. refrence: [foo][foo] +/// case 12. refrence: [foo][bar] +/// case 13. collapsed link: [foo][] +/// case 14. shortcut link: [foo] +/// case 15. inline without URL: [foo]() +/// case 16. just escaped text: \[foo] +/// case 17. inline link: [Foo](foo::Foo) +/// +/// [`Result`]: ../../std/result/enum.Result.html +/// [^example]: https://www.example.com/ +pub fn fo<|>o() {} +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + pub fn foo() + ``` + + --- + + Test cases: + case 1. bare URL: https://www.example.com/ + case 2. inline URL with title: [example](https://www.example.com/) + case 3. code refrence: `Result` + case 4. code refrence but miss footnote: `String` + case 5. autolink: http://www.example.com/ + case 6. email address: test@example.com + case 7. refrence: example + case 8. collapsed link: example + case 9. shortcut link: example + case 10. inline without URL: example + case 11. refrence: foo + case 12. refrence: foo + case 13. collapsed link: foo + case 14. shortcut link: foo + case 15. inline without URL: foo + case 16. just escaped text: \[foo] + case 17. inline link: Foo + + [^example]: https://www.example.com/ + "#]], + ); + } + #[test] fn test_hover_macro_generated_struct_fn_doc_comment() { mark::check!(hover_macro_generated_struct_fn_doc_comment); 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 { } /// Returns a short text describing element at position. - pub fn hover(&self, position: FilePosition) -> Cancelable>> { - self.with_db(|db| hover::hover(db, position)) + pub fn hover( + &self, + position: FilePosition, + links_in_hover: bool, + ) -> Cancelable>> { + self.with_db(|db| hover::hover(db, position, links_in_hover)) } /// 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 @@ use hir::{Adt, Crate, HasAttrs, ModuleDef}; use ide_db::{defs::Definition, RootDatabase}; -use pulldown_cmark::{CowStr, Event, Options, Parser, Tag}; +use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag}; use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; use url::Url; @@ -45,6 +45,41 @@ pub fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) out } +/// Remove all links in markdown documentation. +pub fn remove_links(markdown: &str) -> String { + let mut drop_link = false; + + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_FOOTNOTES); + + let doc = Parser::new_with_broken_link_callback( + markdown, + opts, + Some(&|_, _| Some((String::new(), String::new()))), + ); + let doc = doc.filter_map(move |evt| match evt { + Event::Start(Tag::Link(link_type, ref target, ref title)) => { + if link_type == LinkType::Inline && target.contains("://") { + Some(Event::Start(Tag::Link(link_type, target.clone(), title.clone()))) + } else { + drop_link = true; + None + } + } + Event::End(_) if drop_link => { + drop_link = false; + None + } + _ => Some(evt), + }); + + let mut out = String::new(); + let mut options = CmarkOptions::default(); + options.code_block_backticks = 3; + cmark_with_options(doc, &mut out, None, options).ok(); + out +} + fn rewrite_intra_doc_link( db: &RootDatabase, def: Definition, -- cgit v1.2.3