mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 21:05:02 +00:00
Add hover config linksInHover
to suppress links
This commit is contained in:
parent
18c62c8a39
commit
e73ee9dfa2
5 changed files with 149 additions and 13 deletions
|
@ -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<RangeInfo<HoverResult>> {
|
||||
pub(crate) fn hover(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
links_in_hover: bool,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
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<RangeIn
|
|||
};
|
||||
if let Some(definition) = definition {
|
||||
if let Some(markup) = hover_for_definition(db, definition) {
|
||||
let markup = rewrite_links(db, &markup.as_str(), &definition);
|
||||
let markup = if links_in_hover {
|
||||
rewrite_links(db, &markup.as_str(), &definition)
|
||||
} else {
|
||||
remove_links(&markup.as_str())
|
||||
};
|
||||
res.markup = Markup::from(markup);
|
||||
if let Some(action) = show_implementations_action(db, definition) {
|
||||
res.actions.push(action);
|
||||
|
@ -363,12 +383,23 @@ mod tests {
|
|||
|
||||
fn check_hover_no_result(ra_fixture: &str) {
|
||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
||||
assert!(analysis.hover(position).unwrap().is_none());
|
||||
assert!(analysis.hover(position, true).unwrap().is_none());
|
||||
}
|
||||
|
||||
fn check(ra_fixture: &str, expect: Expect) {
|
||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
||||
let hover = analysis.hover(position).unwrap().unwrap();
|
||||
let hover = analysis.hover(position, true).unwrap().unwrap();
|
||||
|
||||
let content = analysis.db.file_text(position.file_id);
|
||||
let hovered_element = &content[hover.range];
|
||||
|
||||
let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
|
||||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
|
||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
||||
let hover = analysis.hover(position, false).unwrap().unwrap();
|
||||
|
||||
let content = analysis.db.file_text(position.file_id);
|
||||
let hovered_element = &content[hover.range];
|
||||
|
@ -379,7 +410,7 @@ mod tests {
|
|||
|
||||
fn check_actions(ra_fixture: &str, expect: Expect) {
|
||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
||||
let hover = analysis.hover(position).unwrap().unwrap();
|
||||
let hover = analysis.hover(position, true).unwrap().unwrap();
|
||||
expect.assert_debug_eq(&hover.info.actions)
|
||||
}
|
||||
|
||||
|
@ -1809,6 +1840,70 @@ struct S {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_no_links() {
|
||||
check_hover_no_links(
|
||||
r#"
|
||||
/// 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][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
|
||||
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);
|
||||
|
|
|
@ -370,8 +370,12 @@ impl Analysis {
|
|||
}
|
||||
|
||||
/// Returns a short text describing element at position.
|
||||
pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<HoverResult>>> {
|
||||
self.with_db(|db| hover::hover(db, position))
|
||||
pub fn hover(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
links_in_hover: bool,
|
||||
) -> Cancelable<Option<RangeInfo<HoverResult>>> {
|
||||
self.with_db(|db| hover::hover(db, position, links_in_hover))
|
||||
}
|
||||
|
||||
/// Computes parameter information for the given call expression.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -307,6 +307,7 @@ impl Config {
|
|||
run: data.hoverActions_enable && data.hoverActions_run,
|
||||
debug: data.hoverActions_enable && data.hoverActions_debug,
|
||||
goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
|
||||
links_in_hover: data.hoverActions_linksInHover,
|
||||
};
|
||||
|
||||
log::info!("Config::update() = {:#?}", self);
|
||||
|
@ -451,6 +452,7 @@ config_data! {
|
|||
hoverActions_gotoTypeDef: bool = true,
|
||||
hoverActions_implementations: bool = true,
|
||||
hoverActions_run: bool = true,
|
||||
hoverActions_linksInHover: bool = true,
|
||||
|
||||
inlayHints_chainingHints: bool = true,
|
||||
inlayHints_maxLength: Option<usize> = None,
|
||||
|
|
|
@ -597,7 +597,7 @@ pub(crate) fn handle_hover(
|
|||
) -> Result<Option<lsp_ext::Hover>> {
|
||||
let _p = profile::span("handle_hover");
|
||||
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
||||
let info = match snap.analysis.hover(position)? {
|
||||
let info = match snap.analysis.hover(position, snap.config.hover.links_in_hover)? {
|
||||
None => return Ok(None),
|
||||
Some(info) => info,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue