mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Moved CodeLens to ide crate
This commit is contained in:
parent
935830d05b
commit
185da286d2
7 changed files with 388 additions and 202 deletions
142
crates/ide/src/annotations.rs
Normal file
142
crates/ide/src/annotations.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use hir::Semantics;
|
||||||
|
use ide_db::{
|
||||||
|
base_db::{FileId, FilePosition, FileRange, SourceDatabase},
|
||||||
|
RootDatabase, SymbolKind,
|
||||||
|
};
|
||||||
|
use syntax::TextRange;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
file_structure::file_structure,
|
||||||
|
fn_references::find_all_methods,
|
||||||
|
goto_implementation::goto_implementation,
|
||||||
|
references::find_all_refs,
|
||||||
|
runnables::{runnables, Runnable},
|
||||||
|
NavigationTarget, RunnableKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Feature: Annotations
|
||||||
|
//
|
||||||
|
// Provides user with annotations above items for looking up references or impl blocks
|
||||||
|
// and running/debugging binaries.
|
||||||
|
pub struct Annotation {
|
||||||
|
pub range: TextRange,
|
||||||
|
pub kind: AnnotationKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AnnotationKind {
|
||||||
|
Runnable { debug: bool, runnable: Runnable },
|
||||||
|
HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
|
||||||
|
HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AnnotationConfig {
|
||||||
|
pub binary_target: bool,
|
||||||
|
pub annotate_runnables: bool,
|
||||||
|
pub annotate_impls: bool,
|
||||||
|
pub annotate_references: bool,
|
||||||
|
pub annotate_method_references: bool,
|
||||||
|
pub run: bool,
|
||||||
|
pub debug: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn annotations(
|
||||||
|
db: &RootDatabase,
|
||||||
|
file_id: FileId,
|
||||||
|
config: AnnotationConfig,
|
||||||
|
) -> Vec<Annotation> {
|
||||||
|
let mut annotations = Vec::default();
|
||||||
|
|
||||||
|
if config.annotate_runnables {
|
||||||
|
for runnable in runnables(db, file_id) {
|
||||||
|
if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = runnable.action();
|
||||||
|
let range = runnable.nav.full_range;
|
||||||
|
|
||||||
|
if config.run {
|
||||||
|
annotations.push(Annotation {
|
||||||
|
range,
|
||||||
|
// FIXME: This one allocates without reason if run is enabled, but debug is disabled
|
||||||
|
kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if action.debugee && config.debug {
|
||||||
|
annotations.push(Annotation {
|
||||||
|
range,
|
||||||
|
kind: AnnotationKind::Runnable { debug: true, runnable },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_structure(&db.parse(file_id).tree())
|
||||||
|
.into_iter()
|
||||||
|
.filter(|node| {
|
||||||
|
matches!(
|
||||||
|
node.kind,
|
||||||
|
SymbolKind::Trait
|
||||||
|
| SymbolKind::Struct
|
||||||
|
| SymbolKind::Enum
|
||||||
|
| SymbolKind::Union
|
||||||
|
| SymbolKind::Const
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.for_each(|node| {
|
||||||
|
if config.annotate_impls && node.kind != SymbolKind::Const {
|
||||||
|
annotations.push(Annotation {
|
||||||
|
range: node.node_range,
|
||||||
|
kind: AnnotationKind::HasImpls {
|
||||||
|
position: FilePosition { file_id, offset: node.navigation_range.start() },
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.annotate_references {
|
||||||
|
annotations.push(Annotation {
|
||||||
|
range: node.node_range,
|
||||||
|
kind: AnnotationKind::HasReferences {
|
||||||
|
position: FilePosition { file_id, offset: node.navigation_range.start() },
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if config.annotate_method_references {
|
||||||
|
annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
|
||||||
|
range: method.range,
|
||||||
|
kind: AnnotationKind::HasReferences {
|
||||||
|
position: FilePosition { file_id, offset: method.range.start() },
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
|
||||||
|
match annotation.kind {
|
||||||
|
AnnotationKind::HasImpls { position, ref mut data } => {
|
||||||
|
*data = goto_implementation(db, position).map(|range| range.info);
|
||||||
|
}
|
||||||
|
AnnotationKind::HasReferences { position, ref mut data } => {
|
||||||
|
*data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
|
||||||
|
result
|
||||||
|
.references
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, access)| access.into_iter())
|
||||||
|
.flatten()
|
||||||
|
.map(|(range, _)| FileRange { file_id: position.file_id, range })
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
annotation
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ mod markup;
|
||||||
mod prime_caches;
|
mod prime_caches;
|
||||||
mod display;
|
mod display;
|
||||||
|
|
||||||
|
mod annotations;
|
||||||
mod call_hierarchy;
|
mod call_hierarchy;
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
mod expand_macro;
|
mod expand_macro;
|
||||||
|
@ -63,6 +64,7 @@ use syntax::SourceFile;
|
||||||
use crate::display::ToNav;
|
use crate::display::ToNav;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
|
annotations::{Annotation, AnnotationConfig, AnnotationKind},
|
||||||
call_hierarchy::CallItem,
|
call_hierarchy::CallItem,
|
||||||
diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
|
diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
|
||||||
display::navigation_target::NavigationTarget,
|
display::navigation_target::NavigationTarget,
|
||||||
|
@ -555,6 +557,18 @@ impl Analysis {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn annotations(
|
||||||
|
&self,
|
||||||
|
file_id: FileId,
|
||||||
|
config: AnnotationConfig,
|
||||||
|
) -> Cancelable<Vec<Annotation>> {
|
||||||
|
self.with_db(|db| annotations::annotations(db, file_id, config))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> {
|
||||||
|
self.with_db(|db| annotations::resolve_annotation(db, annotation))
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs an operation on that may be Canceled.
|
/// Performs an operation on that may be Canceled.
|
||||||
fn with_db<F, T>(&self, f: F) -> Cancelable<T>
|
fn with_db<F, T>(&self, f: F) -> Cancelable<T>
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//! Conversion lsp_types types to rust-analyzer specific ones.
|
//! Conversion lsp_types types to rust-analyzer specific ones.
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use ide::{AssistKind, LineCol, LineIndex};
|
use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex};
|
||||||
use ide_db::base_db::{FileId, FilePosition, FileRange};
|
use ide_db::base_db::{FileId, FilePosition, FileRange};
|
||||||
use syntax::{TextRange, TextSize};
|
use syntax::{TextRange, TextSize};
|
||||||
use vfs::AbsPathBuf;
|
use vfs::AbsPathBuf;
|
||||||
|
|
||||||
use crate::{global_state::GlobalStateSnapshot, Result};
|
use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result};
|
||||||
|
|
||||||
pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> {
|
pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> {
|
||||||
let path = url.to_file_path().map_err(|()| "url is not a file")?;
|
let path = url.to_file_path().map_err(|()| "url is not a file")?;
|
||||||
|
@ -66,3 +66,39 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
|
||||||
|
|
||||||
Some(assist_kind)
|
Some(assist_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn annotation(
|
||||||
|
world: &GlobalStateSnapshot,
|
||||||
|
code_lens: lsp_types::CodeLens,
|
||||||
|
) -> Result<Annotation> {
|
||||||
|
let data = code_lens.data.unwrap();
|
||||||
|
let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", data)?;
|
||||||
|
|
||||||
|
match resolve {
|
||||||
|
lsp_ext::CodeLensResolveData::Impls(params) => {
|
||||||
|
let file_id =
|
||||||
|
world.url_to_file_id(¶ms.text_document_position_params.text_document.uri)?;
|
||||||
|
let line_index = world.analysis.file_line_index(file_id)?;
|
||||||
|
|
||||||
|
Ok(Annotation {
|
||||||
|
range: text_range(&line_index, code_lens.range),
|
||||||
|
kind: AnnotationKind::HasImpls {
|
||||||
|
position: file_position(world, params.text_document_position_params)?,
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
lsp_ext::CodeLensResolveData::References(params) => {
|
||||||
|
let file_id = world.url_to_file_id(¶ms.text_document.uri)?;
|
||||||
|
let line_index = world.analysis.file_line_index(file_id)?;
|
||||||
|
|
||||||
|
Ok(Annotation {
|
||||||
|
range: text_range(&line_index, code_lens.range),
|
||||||
|
kind: AnnotationKind::HasReferences {
|
||||||
|
position: file_position(world, params)?,
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,8 +9,9 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ide::{
|
use ide::{
|
||||||
FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget,
|
AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex,
|
||||||
Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit,
|
NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange,
|
||||||
|
TextEdit,
|
||||||
};
|
};
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -35,7 +36,7 @@ use crate::{
|
||||||
cargo_target_spec::CargoTargetSpec,
|
cargo_target_spec::CargoTargetSpec,
|
||||||
config::RustfmtConfig,
|
config::RustfmtConfig,
|
||||||
diff::diff,
|
diff::diff,
|
||||||
from_json, from_proto,
|
from_proto,
|
||||||
global_state::{GlobalState, GlobalStateSnapshot},
|
global_state::{GlobalState, GlobalStateSnapshot},
|
||||||
line_endings::LineEndings,
|
line_endings::LineEndings,
|
||||||
lsp_ext::{self, InlayHint, InlayHintsParams},
|
lsp_ext::{self, InlayHint, InlayHintsParams},
|
||||||
|
@ -1078,177 +1079,51 @@ pub(crate) fn handle_code_lens(
|
||||||
params: lsp_types::CodeLensParams,
|
params: lsp_types::CodeLensParams,
|
||||||
) -> Result<Option<Vec<CodeLens>>> {
|
) -> Result<Option<Vec<CodeLens>>> {
|
||||||
let _p = profile::span("handle_code_lens");
|
let _p = profile::span("handle_code_lens");
|
||||||
let mut lenses: Vec<CodeLens> = Default::default();
|
|
||||||
|
|
||||||
let lens_config = snap.config.lens();
|
let lens_config = snap.config.lens();
|
||||||
if lens_config.none() {
|
if lens_config.none() {
|
||||||
// early return before any db query!
|
// early return before any db query!
|
||||||
return Ok(Some(lenses));
|
return Ok(Some(Vec::default()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
||||||
let line_index = snap.analysis.file_line_index(file_id)?;
|
let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?;
|
||||||
let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
|
|
||||||
|
|
||||||
if lens_config.runnable() {
|
let lenses = snap
|
||||||
// Gather runnables
|
.analysis
|
||||||
for runnable in snap.analysis.runnables(file_id)? {
|
.annotations(
|
||||||
if should_skip_target(&runnable, cargo_spec.as_ref()) {
|
file_id,
|
||||||
continue;
|
AnnotationConfig {
|
||||||
}
|
binary_target: cargo_target_spec
|
||||||
|
.map(|spec| {
|
||||||
let action = runnable.action();
|
matches!(
|
||||||
let range = to_proto::range(&line_index, runnable.nav.full_range);
|
spec.target_kind,
|
||||||
let r = to_proto::runnable(&snap, file_id, runnable)?;
|
TargetKind::Bin | TargetKind::Example | TargetKind::Test
|
||||||
if lens_config.run {
|
)
|
||||||
let lens = CodeLens {
|
|
||||||
range,
|
|
||||||
command: Some(run_single_command(&r, action.run_title)),
|
|
||||||
data: None,
|
|
||||||
};
|
|
||||||
lenses.push(lens);
|
|
||||||
}
|
|
||||||
|
|
||||||
if action.debugee && lens_config.debug {
|
|
||||||
let debug_lens =
|
|
||||||
CodeLens { range, command: Some(debug_single_command(&r)), data: None };
|
|
||||||
lenses.push(debug_lens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if lens_config.implementations || lens_config.refs {
|
|
||||||
snap.analysis
|
|
||||||
.file_structure(file_id)?
|
|
||||||
.into_iter()
|
|
||||||
.filter(|it| {
|
|
||||||
matches!(
|
|
||||||
it.kind,
|
|
||||||
SymbolKind::Trait | SymbolKind::Struct | SymbolKind::Enum | SymbolKind::Union
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.for_each(|it| {
|
|
||||||
let range = to_proto::range(&line_index, it.node_range);
|
|
||||||
let position = to_proto::position(&line_index, it.navigation_range.start());
|
|
||||||
let doc_pos = lsp_types::TextDocumentPositionParams::new(
|
|
||||||
params.text_document.clone(),
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
let goto_params = lsp_types::request::GotoImplementationParams {
|
|
||||||
text_document_position_params: doc_pos.clone(),
|
|
||||||
work_done_progress_params: Default::default(),
|
|
||||||
partial_result_params: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if lens_config.implementations {
|
|
||||||
lenses.push(CodeLens {
|
|
||||||
range,
|
|
||||||
command: None,
|
|
||||||
data: Some(to_value(CodeLensResolveData::Impls(goto_params)).unwrap()),
|
|
||||||
})
|
})
|
||||||
}
|
.unwrap_or(false),
|
||||||
|
annotate_runnables: lens_config.runnable(),
|
||||||
if lens_config.refs {
|
annotate_impls: lens_config.implementations,
|
||||||
lenses.push(CodeLens {
|
annotate_references: lens_config.refs,
|
||||||
range,
|
annotate_method_references: lens_config.method_refs,
|
||||||
command: None,
|
run: lens_config.run,
|
||||||
data: Some(to_value(CodeLensResolveData::References(doc_pos)).unwrap()),
|
debug: lens_config.debug,
|
||||||
})
|
},
|
||||||
}
|
)?
|
||||||
});
|
.into_iter()
|
||||||
}
|
.map(|annotation| to_proto::code_lens(&snap, annotation).unwrap())
|
||||||
|
.collect();
|
||||||
if lens_config.method_refs {
|
|
||||||
lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| {
|
|
||||||
let range = to_proto::range(&line_index, it.range);
|
|
||||||
let position = to_proto::position(&line_index, it.range.start());
|
|
||||||
let lens_params =
|
|
||||||
lsp_types::TextDocumentPositionParams::new(params.text_document.clone(), position);
|
|
||||||
|
|
||||||
CodeLens {
|
|
||||||
range,
|
|
||||||
command: None,
|
|
||||||
data: Some(to_value(CodeLensResolveData::References(lens_params)).unwrap()),
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(lenses))
|
Ok(Some(lenses))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
enum CodeLensResolveData {
|
|
||||||
Impls(lsp_types::request::GotoImplementationParams),
|
|
||||||
References(lsp_types::TextDocumentPositionParams),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn handle_code_lens_resolve(
|
pub(crate) fn handle_code_lens_resolve(
|
||||||
snap: GlobalStateSnapshot,
|
snap: GlobalStateSnapshot,
|
||||||
code_lens: CodeLens,
|
code_lens: CodeLens,
|
||||||
) -> Result<CodeLens> {
|
) -> Result<CodeLens> {
|
||||||
let _p = profile::span("handle_code_lens_resolve");
|
let annotation = from_proto::annotation(&snap, code_lens)?;
|
||||||
let data = code_lens.data.unwrap();
|
|
||||||
let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
|
|
||||||
match resolve {
|
|
||||||
Some(CodeLensResolveData::Impls(lens_params)) => {
|
|
||||||
let locations: Vec<Location> =
|
|
||||||
match handle_goto_implementation(snap, lens_params.clone())? {
|
|
||||||
Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
|
|
||||||
Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
|
|
||||||
Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
|
|
||||||
.into_iter()
|
|
||||||
.map(|link| Location::new(link.target_uri, link.target_selection_range))
|
|
||||||
.collect(),
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let title = implementation_title(locations.len());
|
Ok(to_proto::code_lens(&snap, snap.analysis.resolve_annotation(annotation)?)?)
|
||||||
let cmd = show_references_command(
|
|
||||||
title,
|
|
||||||
&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 })
|
|
||||||
}
|
|
||||||
Some(CodeLensResolveData::References(doc_position)) => {
|
|
||||||
let position = from_proto::file_position(&snap, doc_position.clone())?;
|
|
||||||
let locations = snap
|
|
||||||
.analysis
|
|
||||||
.find_all_refs(position, None)
|
|
||||||
.unwrap_or(None)
|
|
||||||
.map(|r| {
|
|
||||||
r.references
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|(file_id, ranges)| {
|
|
||||||
ranges.into_iter().map(move |(range, _)| FileRange { file_id, range })
|
|
||||||
})
|
|
||||||
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
|
||||||
.collect_vec()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let title = reference_title(locations.len());
|
|
||||||
let cmd = if locations.is_empty() {
|
|
||||||
Command { title, command: "".into(), arguments: None }
|
|
||||||
} else {
|
|
||||||
show_references_command(
|
|
||||||
title,
|
|
||||||
&doc_position.text_document.uri,
|
|
||||||
code_lens.range.start,
|
|
||||||
locations,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
|
|
||||||
}
|
|
||||||
None => Ok(CodeLens {
|
|
||||||
range: code_lens.range,
|
|
||||||
command: Some(Command { title: "Error".into(), ..Default::default() }),
|
|
||||||
data: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_document_highlight(
|
pub(crate) fn handle_document_highlight(
|
||||||
|
@ -1547,43 +1422,6 @@ pub(crate) fn handle_open_cargo_toml(
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implementation_title(count: usize) -> String {
|
|
||||||
if count == 1 {
|
|
||||||
"1 implementation".into()
|
|
||||||
} else {
|
|
||||||
format!("{} implementations", count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reference_title(count: usize) -> String {
|
|
||||||
if count == 1 {
|
|
||||||
"1 reference".into()
|
|
||||||
} else {
|
|
||||||
format!("{} references", 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 run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
|
fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
|
||||||
Command {
|
Command {
|
||||||
title: title.to_string(),
|
title: title.to_string(),
|
||||||
|
@ -1635,8 +1473,8 @@ fn show_impl_command_link(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
|
.filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
|
||||||
.collect();
|
.collect();
|
||||||
let title = implementation_title(locations.len());
|
let title = to_proto::implementation_title(locations.len());
|
||||||
let command = show_references_command(title, &uri, position, locations);
|
let command = to_proto::show_references_command(title, &uri, position, locations);
|
||||||
|
|
||||||
return Some(lsp_ext::CommandLinkGroup {
|
return Some(lsp_ext::CommandLinkGroup {
|
||||||
commands: vec![to_command_link(command, "Go to implementations".into())],
|
commands: vec![to_command_link(command, "Go to implementations".into())],
|
||||||
|
|
|
@ -377,3 +377,11 @@ impl Request for OpenCargoToml {
|
||||||
pub struct OpenCargoTomlParams {
|
pub struct OpenCargoTomlParams {
|
||||||
pub text_document: TextDocumentIdentifier,
|
pub text_document: TextDocumentIdentifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about CodeLens, that is to be resolved.
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) enum CodeLensResolveData {
|
||||||
|
Impls(lsp_types::request::GotoImplementationParams),
|
||||||
|
References(lsp_types::TextDocumentPositionParams),
|
||||||
|
}
|
||||||
|
|
|
@ -5,13 +5,15 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ide::{
|
use ide::{
|
||||||
Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
|
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
|
||||||
FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel,
|
Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct,
|
||||||
InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess,
|
HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup,
|
||||||
RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize,
|
NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit,
|
||||||
|
TextRange, TextSize,
|
||||||
};
|
};
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use serde_json::to_value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot,
|
cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot,
|
||||||
|
@ -863,6 +865,141 @@ pub(crate) fn runnable(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn code_lens(
|
||||||
|
snap: &GlobalStateSnapshot,
|
||||||
|
annotation: Annotation,
|
||||||
|
) -> Result<lsp_types::CodeLens> {
|
||||||
|
match annotation.kind {
|
||||||
|
AnnotationKind::Runnable { debug, runnable: run } => {
|
||||||
|
let line_index = snap.analysis.file_line_index(run.nav.file_id)?;
|
||||||
|
let annotation_range = range(&line_index, annotation.range);
|
||||||
|
|
||||||
|
let action = run.action();
|
||||||
|
let r = runnable(&snap, run.nav.file_id, run)?;
|
||||||
|
|
||||||
|
let command = if debug {
|
||||||
|
lsp_types::Command {
|
||||||
|
title: action.run_title.to_string(),
|
||||||
|
command: "rust-analyzer.runSingle".into(),
|
||||||
|
arguments: Some(vec![to_value(r).unwrap()]),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lsp_types::Command {
|
||||||
|
title: "Debug".into(),
|
||||||
|
command: "rust-analyzer.debugSingle".into(),
|
||||||
|
arguments: Some(vec![to_value(r).unwrap()]),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None })
|
||||||
|
}
|
||||||
|
AnnotationKind::HasImpls { position: file_position, data } => {
|
||||||
|
let line_index = snap.analysis.file_line_index(file_position.file_id)?;
|
||||||
|
let annotation_range = range(&line_index, annotation.range);
|
||||||
|
let url = url(snap, file_position.file_id);
|
||||||
|
|
||||||
|
let position = position(&line_index, file_position.offset);
|
||||||
|
|
||||||
|
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
|
||||||
|
|
||||||
|
let doc_pos = lsp_types::TextDocumentPositionParams::new(id.clone(), position);
|
||||||
|
|
||||||
|
let goto_params = lsp_types::request::GotoImplementationParams {
|
||||||
|
text_document_position_params: doc_pos.clone(),
|
||||||
|
work_done_progress_params: Default::default(),
|
||||||
|
partial_result_params: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = data.map(|ranges| {
|
||||||
|
let locations: Vec<lsp_types::Location> = ranges
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|target| {
|
||||||
|
location(
|
||||||
|
snap,
|
||||||
|
FileRange { file_id: target.file_id, range: target.full_range },
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
show_references_command(
|
||||||
|
implementation_title(locations.len()),
|
||||||
|
&url,
|
||||||
|
position,
|
||||||
|
locations,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(lsp_types::CodeLens {
|
||||||
|
range: annotation_range,
|
||||||
|
command,
|
||||||
|
data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
AnnotationKind::HasReferences { position: file_position, data } => {
|
||||||
|
let line_index = snap.analysis.file_line_index(file_position.file_id)?;
|
||||||
|
let annotation_range = range(&line_index, annotation.range);
|
||||||
|
let url = url(snap, file_position.file_id);
|
||||||
|
|
||||||
|
let position = position(&line_index, file_position.offset);
|
||||||
|
|
||||||
|
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
|
||||||
|
|
||||||
|
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, position);
|
||||||
|
|
||||||
|
let command = data.map(|ranges| {
|
||||||
|
let locations: Vec<lsp_types::Location> =
|
||||||
|
ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
|
||||||
|
|
||||||
|
show_references_command(reference_title(locations.len()), &url, position, locations)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(lsp_types::CodeLens {
|
||||||
|
range: annotation_range,
|
||||||
|
command,
|
||||||
|
data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn show_references_command(
|
||||||
|
title: String,
|
||||||
|
uri: &lsp_types::Url,
|
||||||
|
position: lsp_types::Position,
|
||||||
|
locations: Vec<lsp_types::Location>,
|
||||||
|
) -> lsp_types::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.
|
||||||
|
|
||||||
|
lsp_types::Command {
|
||||||
|
title,
|
||||||
|
command: "rust-analyzer.showReferences".into(),
|
||||||
|
arguments: Some(vec![
|
||||||
|
to_value(uri).unwrap(),
|
||||||
|
to_value(position).unwrap(),
|
||||||
|
to_value(locations).unwrap(),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn implementation_title(count: usize) -> String {
|
||||||
|
if count == 1 {
|
||||||
|
"1 implementation".into()
|
||||||
|
} else {
|
||||||
|
format!("{} implementations", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reference_title(count: usize) -> String {
|
||||||
|
if count == 1 {
|
||||||
|
"1 reference".into()
|
||||||
|
} else {
|
||||||
|
format!("{} references", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
|
pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
|
||||||
let value = crate::markdown::format_docs(markup.as_str());
|
let value = crate::markdown::format_docs(markup.as_str());
|
||||||
lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
|
lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!---
|
<!---
|
||||||
lsp_ext.rs hash: 8f1ae8530f69e3a3
|
lsp_ext.rs hash: 34aec6bfeaeb97a
|
||||||
|
|
||||||
If you need to change the above hash to make the test pass, please check if you
|
If you need to change the above hash to make the test pass, please check if you
|
||||||
need to adjust this doc as well and ping this issue:
|
need to adjust this doc as well and ping this issue:
|
||||||
|
@ -573,3 +573,14 @@ This request is sent from client to server to open the current project's Cargo.t
|
||||||
```
|
```
|
||||||
|
|
||||||
`experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
|
`experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
|
||||||
|
|
||||||
|
## CodeLens resolve request
|
||||||
|
|
||||||
|
This request is sent from client to server to resolve previously provided CodeLens.
|
||||||
|
|
||||||
|
As an alternative to `any` type in `data` field of `CodeLens`, you may use `CodeLensResolveData`:
|
||||||
|
```typescript
|
||||||
|
interface CodeLensResolveData {
|
||||||
|
data: (DefinitionParams | TextDocumentPositionParams),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
Loading…
Reference in a new issue