Split hover actions config into its own config struct

This commit is contained in:
Lukas Wirth 2021-06-21 21:41:06 +02:00
parent 8b3d93ee29
commit 99c95b8fa1
4 changed files with 110 additions and 81 deletions

View file

@ -1,5 +1,5 @@
use either::Either;
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay};
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
use ide_db::{
base_db::SourceDatabase,
defs::{Definition, NameClass, NameRefClass},
@ -30,41 +30,11 @@ use crate::{
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
pub implementations: bool,
pub references: bool,
pub run: bool,
pub debug: bool,
pub goto_type_def: bool,
pub links_in_hover: bool,
pub markdown: bool,
pub documentation: bool,
}
impl HoverConfig {
pub const NO_ACTIONS: Self = Self {
implementations: false,
references: false,
run: false,
debug: false,
goto_type_def: false,
links_in_hover: true,
markdown: true,
documentation: true,
};
pub fn any_actions(&self) -> bool {
self.implementations || self.references || self.runnable() || self.goto_type_def
}
pub fn no_actions(&self) -> bool {
!self.any_actions()
}
pub fn runnable(&self) -> bool {
self.run || self.debug
}
}
#[derive(Debug, Clone)]
pub enum HoverAction {
Runnable(Runnable),
@ -95,9 +65,7 @@ pub struct HoverResult {
pub(crate) fn hover(
db: &RootDatabase,
position: FilePosition,
links_in_hover: bool,
documentation: bool,
markdown: bool,
config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
let sema = hir::Semantics::new(db);
let file = sema.parse(position.file_id).syntax().clone();
@ -156,10 +124,14 @@ pub(crate) fn hover(
}
_ => None,
};
if let Some(markup) =
hover_for_definition(db, definition, famous_defs.as_ref(), documentation)
{
res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref(), config) {
res.markup = process_markup(
sema.db,
definition,
&markup,
config.links_in_hover,
config.markdown,
);
if let Some(action) = show_implementations_action(db, definition) {
res.actions.push(action);
}
@ -181,8 +153,7 @@ pub(crate) fn hover(
}
}
if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, documentation, &token)
{
if let res @ Some(_) = hover_for_keyword(&sema, config, &token) {
return res;
}
@ -201,7 +172,7 @@ pub(crate) fn hover(
}
};
res.markup = if markdown {
res.markup = if config.markdown {
Markup::fenced_block(&ty.display(db))
} else {
ty.display(db).to_string().into()
@ -428,7 +399,7 @@ fn hover_for_definition(
db: &RootDatabase,
def: Definition,
famous_defs: Option<&FamousDefs>,
documentation: bool,
config: &HoverConfig,
) -> Option<Markup> {
let mod_path = definition_mod_path(db, &def);
let (label, docs) = match def {
@ -466,7 +437,7 @@ fn hover_for_definition(
Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
};
return hover_markup(docs.filter(|_| documentation).map(Into::into), label, mod_path);
return hover_markup(docs.filter(|_| config.documentation).map(Into::into), label, mod_path);
fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
where
@ -502,13 +473,11 @@ fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
}
fn hover_for_keyword(
sema: &hir::Semantics<RootDatabase>,
links_in_hover: bool,
markdown: bool,
documentation: bool,
sema: &Semantics<RootDatabase>,
config: &HoverConfig,
token: &SyntaxToken,
) -> Option<RangeInfo<HoverResult>> {
if !token.kind().is_keyword() || !documentation {
if !token.kind().is_keyword() || !config.documentation {
return None;
}
let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
@ -520,8 +489,8 @@ fn hover_for_keyword(
sema.db,
Definition::ModuleDef(doc_owner.into()),
&hover_markup(Some(docs.into()), token.text().into(), None)?,
links_in_hover,
markdown,
config.links_in_hover,
config.markdown,
);
Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
}
@ -561,16 +530,28 @@ mod tests {
use expect_test::{expect, Expect};
use ide_db::base_db::FileLoader;
use crate::fixture;
use crate::{fixture, HoverConfig};
fn check_hover_no_result(ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
assert!(analysis.hover(position, true, true, true).unwrap().is_none());
assert!(analysis
.hover(
position,
&HoverConfig { links_in_hover: true, markdown: true, documentation: true }
)
.unwrap()
.is_none());
}
fn check(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis.hover(position, true, true, true).unwrap().unwrap();
let hover = analysis
.hover(
position,
&HoverConfig { links_in_hover: true, markdown: true, documentation: true },
)
.unwrap()
.unwrap();
let content = analysis.db.file_text(position.file_id);
let hovered_element = &content[hover.range];
@ -581,7 +562,13 @@ mod tests {
fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis.hover(position, false, true, true).unwrap().unwrap();
let hover = analysis
.hover(
position,
&HoverConfig { links_in_hover: false, markdown: true, documentation: true },
)
.unwrap()
.unwrap();
let content = analysis.db.file_text(position.file_id);
let hovered_element = &content[hover.range];
@ -592,7 +579,13 @@ mod tests {
fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis.hover(position, true, true, false).unwrap().unwrap();
let hover = analysis
.hover(
position,
&HoverConfig { links_in_hover: true, markdown: false, documentation: true },
)
.unwrap()
.unwrap();
let content = analysis.db.file_text(position.file_id);
let hovered_element = &content[hover.range];
@ -603,7 +596,13 @@ mod tests {
fn check_actions(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis.hover(position, true, true, true).unwrap().unwrap();
let hover = analysis
.hover(
position,
&HoverConfig { links_in_hover: true, markdown: true, documentation: true },
)
.unwrap()
.unwrap();
expect.assert_debug_eq(&hover.info.actions)
}

View file

@ -408,11 +408,9 @@ impl Analysis {
pub fn hover(
&self,
position: FilePosition,
links_in_hover: bool,
documentation: bool,
markdown: bool,
config: &HoverConfig,
) -> Cancellable<Option<RangeInfo<HoverResult>>> {
self.with_db(|db| hover::hover(db, position, links_in_hover, documentation, markdown))
self.with_db(|db| hover::hover(db, position, config))
}
/// Return URL(s) for the documentation of the symbol under the cursor.

View file

@ -32,6 +32,9 @@ use crate::{
//
// However, editor specific config, which the server doesn't know about, should
// be specified directly in `package.json`.
//
// To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep
// parsing the old name.
config_data! {
struct ConfigData {
/// How imports should be grouped into use statements.
@ -309,6 +312,37 @@ impl LensConfig {
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverActionsConfig {
pub implementations: bool,
pub references: bool,
pub run: bool,
pub debug: bool,
pub goto_type_def: bool,
}
impl HoverActionsConfig {
pub const NO_ACTIONS: Self = Self {
implementations: false,
references: false,
run: false,
debug: false,
goto_type_def: false,
};
pub fn any(&self) -> bool {
self.implementations || self.references || self.runnable() || self.goto_type_def
}
pub fn none(&self) -> bool {
!self.any()
}
pub fn runnable(&self) -> bool {
self.run || self.debug
}
}
#[derive(Debug, Clone)]
pub struct FilesConfig {
pub watcher: FilesWatcher,
@ -527,7 +561,7 @@ impl Config {
pub fn code_action_group(&self) -> bool {
self.experimental("codeActionGroup")
}
pub fn hover_actions(&self) -> bool {
pub fn experimental_hover_actions(&self) -> bool {
self.experimental("hoverActions")
}
pub fn server_status_notification(&self) -> bool {
@ -727,17 +761,21 @@ impl Config {
refs: self.data.lens_enable && self.data.lens_references,
}
}
pub fn highlighting_strings(&self) -> bool {
self.data.highlighting_strings
}
pub fn hover(&self) -> HoverConfig {
HoverConfig {
pub fn hover_actions(&self) -> HoverActionsConfig {
HoverActionsConfig {
implementations: self.data.hoverActions_enable
&& self.data.hoverActions_implementations,
references: self.data.hoverActions_enable && self.data.hoverActions_references,
run: self.data.hoverActions_enable && self.data.hoverActions_run,
debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
}
}
pub fn highlighting_strings(&self) -> bool {
self.data.highlighting_strings
}
pub fn hover(&self) -> HoverConfig {
HoverConfig {
links_in_hover: self.data.hover_linksInHover,
markdown: try_or!(
self.caps

View file

@ -861,13 +861,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 hover_config = snap.config.hover();
let info = match snap.analysis.hover(
position,
hover_config.links_in_hover,
hover_config.documentation,
hover_config.markdown,
)? {
let info = match snap.analysis.hover(position, &snap.config.hover())? {
None => return Ok(None),
Some(info) => info,
};
@ -1487,7 +1481,7 @@ fn show_impl_command_link(
snap: &GlobalStateSnapshot,
position: &FilePosition,
) -> Option<lsp_ext::CommandLinkGroup> {
if snap.config.hover().implementations {
if snap.config.hover_actions().implementations {
if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
let uri = to_proto::url(snap, position.file_id);
let line_index = snap.file_line_index(position.file_id).ok()?;
@ -1513,7 +1507,7 @@ fn show_ref_command_link(
snap: &GlobalStateSnapshot,
position: &FilePosition,
) -> Option<lsp_ext::CommandLinkGroup> {
if snap.config.hover().references {
if snap.config.hover_actions().references {
if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
let uri = to_proto::url(snap, position.file_id);
let line_index = snap.file_line_index(position.file_id).ok()?;
@ -1544,8 +1538,8 @@ fn runnable_action_links(
runnable: Runnable,
) -> Option<lsp_ext::CommandLinkGroup> {
let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
let hover_config = snap.config.hover();
if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
let hover_actions_config = snap.config.hover_actions();
if !hover_actions_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
return None;
}
@ -1553,12 +1547,12 @@ fn runnable_action_links(
to_proto::runnable(snap, runnable).ok().map(|r| {
let mut group = lsp_ext::CommandLinkGroup::default();
if hover_config.run {
if hover_actions_config.run {
let run_command = to_proto::command::run_single(&r, action.run_title);
group.commands.push(to_command_link(run_command, r.label.clone()));
}
if hover_config.debug {
if hover_actions_config.debug {
let dbg_command = to_proto::command::debug_single(&r);
group.commands.push(to_command_link(dbg_command, r.label));
}
@ -1571,7 +1565,7 @@ fn goto_type_action_links(
snap: &GlobalStateSnapshot,
nav_targets: &[HoverGotoTypeData],
) -> Option<lsp_ext::CommandLinkGroup> {
if !snap.config.hover().goto_type_def || nav_targets.is_empty() {
if !snap.config.hover_actions().goto_type_def || nav_targets.is_empty() {
return None;
}
@ -1591,7 +1585,7 @@ fn prepare_hover_actions(
snap: &GlobalStateSnapshot,
actions: &[HoverAction],
) -> Vec<lsp_ext::CommandLinkGroup> {
if snap.config.hover().no_actions() || !snap.config.hover_actions() {
if snap.config.hover_actions().none() || !snap.config.experimental_hover_actions() {
return Vec::new();
}