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},
#[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),
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();
}
_ => 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);
}
}
}
- 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;
}
}
};
- res.markup = if markdown {
+ res.markup = if config.markdown {
Markup::fenced_block(&ty.display(db))
} else {
ty.display(db).to_string().into()
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 {
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
}
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());
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() }))
}
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];
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];
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];
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)
}
//
// 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.
}
}
+#[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,
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 {
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
) -> 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,
};
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()?;
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()?;
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;
}
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));
}
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;
}
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();
}