mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
4729: Hover actions r=matklad a=vsrs This PR adds a `hoverActions` LSP extension and a `Go to Implementations` action as an example:  4748: Add an `ImportMap` and use it to resolve item paths in `find_path` r=matklad a=jonas-schievink Removes the "go faster" queries I added in https://github.com/rust-analyzer/rust-analyzer/pull/4501 and https://github.com/rust-analyzer/rust-analyzer/pull/4506. I've checked this PR on the rustc code base and the assists are still fast. This should fix https://github.com/rust-analyzer/rust-analyzer/issues/4515. Note that this does introduce a change in behavior: We now always refer to items defined in external crates using paths through the external crate. Previously we could also use a local path (if for example the extern crate was reexported locally), as seen in the changed test. If that is undesired I can fix that, but the test didn't say why the previous behavior would be preferable. Co-authored-by: vsrs <vit@conrlab.com> Co-authored-by: Jonas Schievink <jonasschievink@gmail.com> Co-authored-by: Jonas Schievink <jonas.schievink@ferrous-systems.com>
This commit is contained in:
commit
4029628f15
22 changed files with 899 additions and 169 deletions
|
@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf};
|
|||
|
||||
use lsp_types::ClientCapabilities;
|
||||
use ra_flycheck::FlycheckConfig;
|
||||
use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig};
|
||||
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
|
||||
use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -34,6 +34,7 @@ pub struct Config {
|
|||
pub assist: AssistConfig,
|
||||
pub call_info_full: bool,
|
||||
pub lens: LensConfig,
|
||||
pub hover: HoverConfig,
|
||||
|
||||
pub with_sysroot: bool,
|
||||
pub linked_projects: Vec<LinkedProject>,
|
||||
|
@ -124,6 +125,7 @@ pub struct ClientCapsConfig {
|
|||
pub work_done_progress: bool,
|
||||
pub code_action_group: bool,
|
||||
pub resolve_code_action: bool,
|
||||
pub hover_actions: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -162,6 +164,7 @@ impl Default for Config {
|
|||
assist: AssistConfig::default(),
|
||||
call_info_full: true,
|
||||
lens: LensConfig::default(),
|
||||
hover: HoverConfig::default(),
|
||||
linked_projects: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -278,6 +281,14 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
let mut use_hover_actions = false;
|
||||
set(value, "/hoverActions/enable", &mut use_hover_actions);
|
||||
if use_hover_actions {
|
||||
set(value, "/hoverActions/implementations", &mut self.hover.implementations);
|
||||
} else {
|
||||
self.hover = HoverConfig::NO_ACTIONS;
|
||||
}
|
||||
|
||||
log::info!("Config::update() = {:#?}", self);
|
||||
|
||||
fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
|
||||
|
@ -331,17 +342,15 @@ impl Config {
|
|||
|
||||
self.assist.allow_snippets(false);
|
||||
if let Some(experimental) = &caps.experimental {
|
||||
let snippet_text_edit =
|
||||
experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true);
|
||||
let get_bool =
|
||||
|index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
|
||||
|
||||
let snippet_text_edit = get_bool("snippetTextEdit");
|
||||
self.assist.allow_snippets(snippet_text_edit);
|
||||
|
||||
let code_action_group =
|
||||
experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true);
|
||||
self.client_caps.code_action_group = code_action_group;
|
||||
|
||||
let resolve_code_action =
|
||||
experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true);
|
||||
self.client_caps.resolve_code_action = resolve_code_action;
|
||||
self.client_caps.code_action_group = get_bool("codeActionGroup");
|
||||
self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
|
||||
self.client_caps.hover_actions = get_bool("hoverActions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -260,3 +260,35 @@ pub struct SnippetTextEdit {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub insert_text_format: Option<lsp_types::InsertTextFormat>,
|
||||
}
|
||||
|
||||
pub enum HoverRequest {}
|
||||
|
||||
impl Request for HoverRequest {
|
||||
type Params = lsp_types::HoverParams;
|
||||
type Result = Option<Hover>;
|
||||
const METHOD: &'static str = "textDocument/hover";
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
pub struct Hover {
|
||||
#[serde(flatten)]
|
||||
pub hover: lsp_types::Hover,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub actions: Vec<CommandLinkGroup>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct CommandLinkGroup {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub title: Option<String>,
|
||||
pub commands: Vec<CommandLink>,
|
||||
}
|
||||
|
||||
// LSP v3.15 Command does not have a `tooltip` field, vscode supports one.
|
||||
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct CommandLink {
|
||||
#[serde(flatten)]
|
||||
pub command: lsp_types::Command,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tooltip: Option<String>,
|
||||
}
|
||||
|
|
|
@ -510,6 +510,7 @@ fn on_request(
|
|||
.on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
|
||||
.on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
|
||||
.on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)?
|
||||
.on::<lsp_ext::HoverRequest>(handlers::handle_hover)?
|
||||
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
|
||||
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
|
||||
.on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
|
||||
|
@ -521,7 +522,6 @@ fn on_request(
|
|||
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
|
||||
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
|
||||
.on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)?
|
||||
.on::<lsp_types::request::HoverRequest>(handlers::handle_hover)?
|
||||
.on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)?
|
||||
.on::<lsp_types::request::Rename>(handlers::handle_rename)?
|
||||
.on::<lsp_types::request::References>(handlers::handle_references)?
|
||||
|
|
|
@ -12,13 +12,14 @@ use lsp_types::{
|
|||
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
|
||||
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
|
||||
CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
|
||||
DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
|
||||
MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
|
||||
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
||||
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
|
||||
DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent,
|
||||
MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams,
|
||||
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
|
||||
TextDocumentIdentifier, Url, WorkspaceEdit,
|
||||
};
|
||||
use ra_ide::{
|
||||
FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit,
|
||||
FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope,
|
||||
TextEdit,
|
||||
};
|
||||
use ra_prof::profile;
|
||||
use ra_project_model::TargetKind;
|
||||
|
@ -537,7 +538,7 @@ pub fn handle_signature_help(
|
|||
pub fn handle_hover(
|
||||
snap: GlobalStateSnapshot,
|
||||
params: lsp_types::HoverParams,
|
||||
) -> Result<Option<Hover>> {
|
||||
) -> Result<Option<lsp_ext::Hover>> {
|
||||
let _p = profile("handle_hover");
|
||||
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
||||
let info = match snap.analysis().hover(position)? {
|
||||
|
@ -546,14 +547,18 @@ pub fn handle_hover(
|
|||
};
|
||||
let line_index = snap.analysis.file_line_index(position.file_id)?;
|
||||
let range = to_proto::range(&line_index, info.range);
|
||||
let res = Hover {
|
||||
contents: HoverContents::Markup(MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: crate::markdown::format_docs(&info.info.to_markup()),
|
||||
}),
|
||||
range: Some(range),
|
||||
let hover = lsp_ext::Hover {
|
||||
hover: lsp_types::Hover {
|
||||
contents: HoverContents::Markup(MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: crate::markdown::format_docs(&info.info.to_markup()),
|
||||
}),
|
||||
range: Some(range),
|
||||
},
|
||||
actions: prepare_hover_actions(&snap, info.info.actions()),
|
||||
};
|
||||
Ok(Some(res))
|
||||
|
||||
Ok(Some(hover))
|
||||
}
|
||||
|
||||
pub fn handle_prepare_rename(
|
||||
|
@ -924,24 +929,13 @@ pub fn handle_code_lens_resolve(
|
|||
_ => vec![],
|
||||
};
|
||||
|
||||
let title = if locations.len() == 1 {
|
||||
"1 implementation".into()
|
||||
} else {
|
||||
format!("{} implementations", locations.len())
|
||||
};
|
||||
|
||||
// We cannot use the 'editor.action.showReferences' command directly
|
||||
// because that command requires vscode types which we convert in the handler
|
||||
// on the client side.
|
||||
let cmd = Command {
|
||||
let title = implementation_title(locations.len());
|
||||
let cmd = show_references_command(
|
||||
title,
|
||||
command: "rust-analyzer.showReferences".into(),
|
||||
arguments: Some(vec![
|
||||
to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(),
|
||||
to_value(code_lens.range.start).unwrap(),
|
||||
to_value(locations).unwrap(),
|
||||
]),
|
||||
};
|
||||
&lens_params.text_document_position_params.text_document.uri,
|
||||
code_lens.range.start,
|
||||
locations,
|
||||
);
|
||||
Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
|
||||
}
|
||||
None => Ok(CodeLens {
|
||||
|
@ -1145,3 +1139,78 @@ pub fn handle_semantic_tokens_range(
|
|||
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
|
||||
Ok(Some(semantic_tokens.into()))
|
||||
}
|
||||
|
||||
fn implementation_title(count: usize) -> String {
|
||||
if count == 1 {
|
||||
"1 implementation".into()
|
||||
} else {
|
||||
format!("{} implementations", count)
|
||||
}
|
||||
}
|
||||
|
||||
fn show_references_command(
|
||||
title: String,
|
||||
uri: &lsp_types::Url,
|
||||
position: lsp_types::Position,
|
||||
locations: Vec<lsp_types::Location>,
|
||||
) -> Command {
|
||||
// We cannot use the 'editor.action.showReferences' command directly
|
||||
// because that command requires vscode types which we convert in the handler
|
||||
// on the client side.
|
||||
|
||||
Command {
|
||||
title,
|
||||
command: "rust-analyzer.showReferences".into(),
|
||||
arguments: Some(vec![
|
||||
to_value(uri).unwrap(),
|
||||
to_value(position).unwrap(),
|
||||
to_value(locations).unwrap(),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
|
||||
lsp_ext::CommandLink { tooltip: Some(tooltip), command }
|
||||
}
|
||||
|
||||
fn show_impl_command_link(
|
||||
snap: &GlobalStateSnapshot,
|
||||
position: &FilePosition,
|
||||
) -> Option<lsp_ext::CommandLinkGroup> {
|
||||
if snap.config.hover.implementations {
|
||||
if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) {
|
||||
let uri = to_proto::url(snap, position.file_id).ok()?;
|
||||
let line_index = snap.analysis().file_line_index(position.file_id).ok()?;
|
||||
let position = to_proto::position(&line_index, position.offset);
|
||||
let locations: Vec<_> = nav_data
|
||||
.info
|
||||
.iter()
|
||||
.filter_map(|it| to_proto::location(snap, it.file_range()).ok())
|
||||
.collect();
|
||||
let title = implementation_title(locations.len());
|
||||
let command = show_references_command(title, &uri, position, locations);
|
||||
|
||||
return Some(lsp_ext::CommandLinkGroup {
|
||||
commands: vec![to_command_link(command, "Go to implementations".into())],
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn prepare_hover_actions(
|
||||
snap: &GlobalStateSnapshot,
|
||||
actions: &[HoverAction],
|
||||
) -> Vec<lsp_ext::CommandLinkGroup> {
|
||||
if snap.config.hover.none() || !snap.config.client_caps.hover_actions {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
actions
|
||||
.iter()
|
||||
.filter_map(|it| match it {
|
||||
HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue