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::{
|
use crate::{
|
||||||
display::{macro_label, ShortLabel, ToNav, TryToNav},
|
display::{macro_label, ShortLabel, ToNav, TryToNav},
|
||||||
link_rewrite::rewrite_links,
|
link_rewrite::{remove_links, rewrite_links},
|
||||||
markup::Markup,
|
markup::Markup,
|
||||||
runnables::runnable,
|
runnables::runnable,
|
||||||
FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
|
FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
|
||||||
|
@ -26,17 +26,29 @@ pub struct HoverConfig {
|
||||||
pub run: bool,
|
pub run: bool,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub goto_type_def: bool,
|
pub goto_type_def: bool,
|
||||||
|
pub links_in_hover: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for HoverConfig {
|
impl Default for HoverConfig {
|
||||||
fn default() -> Self {
|
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 {
|
impl HoverConfig {
|
||||||
pub const NO_ACTIONS: Self =
|
pub const NO_ACTIONS: Self = Self {
|
||||||
Self { implementations: false, run: false, debug: false, goto_type_def: false };
|
implementations: false,
|
||||||
|
run: false,
|
||||||
|
debug: false,
|
||||||
|
goto_type_def: false,
|
||||||
|
links_in_hover: true,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn any(&self) -> bool {
|
pub fn any(&self) -> bool {
|
||||||
self.implementations || self.runnable() || self.goto_type_def
|
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.
|
// 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.
|
// 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 sema = Semantics::new(db);
|
||||||
let file = sema.parse(position.file_id).syntax().clone();
|
let file = sema.parse(position.file_id).syntax().clone();
|
||||||
let token = pick_best(file.token_at_offset(position.offset))?;
|
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(definition) = definition {
|
||||||
if let Some(markup) = hover_for_definition(db, 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);
|
res.markup = Markup::from(markup);
|
||||||
if let Some(action) = show_implementations_action(db, definition) {
|
if let Some(action) = show_implementations_action(db, definition) {
|
||||||
res.actions.push(action);
|
res.actions.push(action);
|
||||||
|
@ -363,12 +383,23 @@ mod tests {
|
||||||
|
|
||||||
fn check_hover_no_result(ra_fixture: &str) {
|
fn check_hover_no_result(ra_fixture: &str) {
|
||||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
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) {
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
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 content = analysis.db.file_text(position.file_id);
|
||||||
let hovered_element = &content[hover.range];
|
let hovered_element = &content[hover.range];
|
||||||
|
@ -379,7 +410,7 @@ mod tests {
|
||||||
|
|
||||||
fn check_actions(ra_fixture: &str, expect: Expect) {
|
fn check_actions(ra_fixture: &str, expect: Expect) {
|
||||||
let (analysis, position) = analysis_and_position(ra_fixture);
|
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)
|
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]
|
#[test]
|
||||||
fn test_hover_macro_generated_struct_fn_doc_comment() {
|
fn test_hover_macro_generated_struct_fn_doc_comment() {
|
||||||
mark::check!(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.
|
/// Returns a short text describing element at position.
|
||||||
pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<HoverResult>>> {
|
pub fn hover(
|
||||||
self.with_db(|db| hover::hover(db, position))
|
&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.
|
/// Computes parameter information for the given call expression.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use hir::{Adt, Crate, HasAttrs, ModuleDef};
|
use hir::{Adt, Crate, HasAttrs, ModuleDef};
|
||||||
use ide_db::{defs::Definition, RootDatabase};
|
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 pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -45,6 +45,41 @@ pub fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition)
|
||||||
out
|
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(
|
fn rewrite_intra_doc_link(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
def: Definition,
|
def: Definition,
|
||||||
|
|
|
@ -307,6 +307,7 @@ impl Config {
|
||||||
run: data.hoverActions_enable && data.hoverActions_run,
|
run: data.hoverActions_enable && data.hoverActions_run,
|
||||||
debug: data.hoverActions_enable && data.hoverActions_debug,
|
debug: data.hoverActions_enable && data.hoverActions_debug,
|
||||||
goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
|
goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
|
||||||
|
links_in_hover: data.hoverActions_linksInHover,
|
||||||
};
|
};
|
||||||
|
|
||||||
log::info!("Config::update() = {:#?}", self);
|
log::info!("Config::update() = {:#?}", self);
|
||||||
|
@ -451,6 +452,7 @@ config_data! {
|
||||||
hoverActions_gotoTypeDef: bool = true,
|
hoverActions_gotoTypeDef: bool = true,
|
||||||
hoverActions_implementations: bool = true,
|
hoverActions_implementations: bool = true,
|
||||||
hoverActions_run: bool = true,
|
hoverActions_run: bool = true,
|
||||||
|
hoverActions_linksInHover: bool = true,
|
||||||
|
|
||||||
inlayHints_chainingHints: bool = true,
|
inlayHints_chainingHints: bool = true,
|
||||||
inlayHints_maxLength: Option<usize> = None,
|
inlayHints_maxLength: Option<usize> = None,
|
||||||
|
|
|
@ -597,7 +597,7 @@ pub(crate) fn handle_hover(
|
||||||
) -> Result<Option<lsp_ext::Hover>> {
|
) -> Result<Option<lsp_ext::Hover>> {
|
||||||
let _p = profile::span("handle_hover");
|
let _p = profile::span("handle_hover");
|
||||||
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
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),
|
None => return Ok(None),
|
||||||
Some(info) => info,
|
Some(info) => info,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue