aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorivan770 <[email protected]>2021-02-13 11:07:47 +0000
committerivan770 <[email protected]>2021-02-13 11:07:47 +0000
commit185da286d26ea7f892097c48b79a28acd7e5f172 (patch)
tree925bdb4be5377544323989476b0d30e091287ea5 /crates
parent935830d05bcf5f0c648e636dcbc8848a201467c0 (diff)
Moved CodeLens to ide crate
Diffstat (limited to 'crates')
-rw-r--r--crates/ide/src/annotations.rs142
-rw-r--r--crates/ide/src/lib.rs14
-rw-r--r--crates/rust-analyzer/src/from_proto.rs40
-rw-r--r--crates/rust-analyzer/src/handlers.rs228
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/to_proto.rs145
6 files changed, 376 insertions, 201 deletions
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
new file mode 100644
index 000000000..6d54b6b57
--- /dev/null
+++ b/crates/ide/src/annotations.rs
@@ -0,0 +1,142 @@
1use hir::Semantics;
2use ide_db::{
3 base_db::{FileId, FilePosition, FileRange, SourceDatabase},
4 RootDatabase, SymbolKind,
5};
6use syntax::TextRange;
7
8use crate::{
9 file_structure::file_structure,
10 fn_references::find_all_methods,
11 goto_implementation::goto_implementation,
12 references::find_all_refs,
13 runnables::{runnables, Runnable},
14 NavigationTarget, RunnableKind,
15};
16
17// Feature: Annotations
18//
19// Provides user with annotations above items for looking up references or impl blocks
20// and running/debugging binaries.
21pub struct Annotation {
22 pub range: TextRange,
23 pub kind: AnnotationKind,
24}
25
26pub enum AnnotationKind {
27 Runnable { debug: bool, runnable: Runnable },
28 HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
29 HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
30}
31
32pub struct AnnotationConfig {
33 pub binary_target: bool,
34 pub annotate_runnables: bool,
35 pub annotate_impls: bool,
36 pub annotate_references: bool,
37 pub annotate_method_references: bool,
38 pub run: bool,
39 pub debug: bool,
40}
41
42pub(crate) fn annotations(
43 db: &RootDatabase,
44 file_id: FileId,
45 config: AnnotationConfig,
46) -> Vec<Annotation> {
47 let mut annotations = Vec::default();
48
49 if config.annotate_runnables {
50 for runnable in runnables(db, file_id) {
51 if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target {
52 continue;
53 }
54
55 let action = runnable.action();
56 let range = runnable.nav.full_range;
57
58 if config.run {
59 annotations.push(Annotation {
60 range,
61 // FIXME: This one allocates without reason if run is enabled, but debug is disabled
62 kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
63 });
64 }
65
66 if action.debugee && config.debug {
67 annotations.push(Annotation {
68 range,
69 kind: AnnotationKind::Runnable { debug: true, runnable },
70 });
71 }
72 }
73 }
74
75 file_structure(&db.parse(file_id).tree())
76 .into_iter()
77 .filter(|node| {
78 matches!(
79 node.kind,
80 SymbolKind::Trait
81 | SymbolKind::Struct
82 | SymbolKind::Enum
83 | SymbolKind::Union
84 | SymbolKind::Const
85 )
86 })
87 .for_each(|node| {
88 if config.annotate_impls && node.kind != SymbolKind::Const {
89 annotations.push(Annotation {
90 range: node.node_range,
91 kind: AnnotationKind::HasImpls {
92 position: FilePosition { file_id, offset: node.navigation_range.start() },
93 data: None,
94 },
95 });
96 }
97
98 if config.annotate_references {
99 annotations.push(Annotation {
100 range: node.node_range,
101 kind: AnnotationKind::HasReferences {
102 position: FilePosition { file_id, offset: node.navigation_range.start() },
103 data: None,
104 },
105 });
106 }
107 });
108
109 if config.annotate_method_references {
110 annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
111 range: method.range,
112 kind: AnnotationKind::HasReferences {
113 position: FilePosition { file_id, offset: method.range.start() },
114 data: None,
115 },
116 }));
117 }
118
119 annotations
120}
121
122pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
123 match annotation.kind {
124 AnnotationKind::HasImpls { position, ref mut data } => {
125 *data = goto_implementation(db, position).map(|range| range.info);
126 }
127 AnnotationKind::HasReferences { position, ref mut data } => {
128 *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
129 result
130 .references
131 .into_iter()
132 .map(|(_, access)| access.into_iter())
133 .flatten()
134 .map(|(range, _)| FileRange { file_id: position.file_id, range })
135 .collect()
136 });
137 }
138 _ => {}
139 };
140
141 annotation
142}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 592b12925..89e7bef7d 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -22,6 +22,7 @@ mod markup;
22mod prime_caches; 22mod prime_caches;
23mod display; 23mod display;
24 24
25mod annotations;
25mod call_hierarchy; 26mod call_hierarchy;
26mod diagnostics; 27mod diagnostics;
27mod expand_macro; 28mod expand_macro;
@@ -63,6 +64,7 @@ use syntax::SourceFile;
63use crate::display::ToNav; 64use crate::display::ToNav;
64 65
65pub use crate::{ 66pub use crate::{
67 annotations::{Annotation, AnnotationConfig, AnnotationKind},
66 call_hierarchy::CallItem, 68 call_hierarchy::CallItem,
67 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, 69 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
68 display::navigation_target::NavigationTarget, 70 display::navigation_target::NavigationTarget,
@@ -555,6 +557,18 @@ impl Analysis {
555 }) 557 })
556 } 558 }
557 559
560 pub fn annotations(
561 &self,
562 file_id: FileId,
563 config: AnnotationConfig,
564 ) -> Cancelable<Vec<Annotation>> {
565 self.with_db(|db| annotations::annotations(db, file_id, config))
566 }
567
568 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> {
569 self.with_db(|db| annotations::resolve_annotation(db, annotation))
570 }
571
558 /// Performs an operation on that may be Canceled. 572 /// Performs an operation on that may be Canceled.
559 fn with_db<F, T>(&self, f: F) -> Cancelable<T> 573 fn with_db<F, T>(&self, f: F) -> Cancelable<T>
560 where 574 where
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index aa6b808d6..6676eebf4 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -1,12 +1,12 @@
1//! Conversion lsp_types types to rust-analyzer specific ones. 1//! Conversion lsp_types types to rust-analyzer specific ones.
2use std::convert::TryFrom; 2use std::convert::TryFrom;
3 3
4use ide::{AssistKind, LineCol, LineIndex}; 4use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex};
5use ide_db::base_db::{FileId, FilePosition, FileRange}; 5use ide_db::base_db::{FileId, FilePosition, FileRange};
6use syntax::{TextRange, TextSize}; 6use syntax::{TextRange, TextSize};
7use vfs::AbsPathBuf; 7use vfs::AbsPathBuf;
8 8
9use crate::{global_state::GlobalStateSnapshot, Result}; 9use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result};
10 10
11pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> { 11pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> {
12 let path = url.to_file_path().map_err(|()| "url is not a file")?; 12 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>
66 66
67 Some(assist_kind) 67 Some(assist_kind)
68} 68}
69
70pub(crate) fn annotation(
71 world: &GlobalStateSnapshot,
72 code_lens: lsp_types::CodeLens,
73) -> Result<Annotation> {
74 let data = code_lens.data.unwrap();
75 let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", data)?;
76
77 match resolve {
78 lsp_ext::CodeLensResolveData::Impls(params) => {
79 let file_id =
80 world.url_to_file_id(&params.text_document_position_params.text_document.uri)?;
81 let line_index = world.analysis.file_line_index(file_id)?;
82
83 Ok(Annotation {
84 range: text_range(&line_index, code_lens.range),
85 kind: AnnotationKind::HasImpls {
86 position: file_position(world, params.text_document_position_params)?,
87 data: None,
88 },
89 })
90 }
91 lsp_ext::CodeLensResolveData::References(params) => {
92 let file_id = world.url_to_file_id(&params.text_document.uri)?;
93 let line_index = world.analysis.file_line_index(file_id)?;
94
95 Ok(Annotation {
96 range: text_range(&line_index, code_lens.range),
97 kind: AnnotationKind::HasReferences {
98 position: file_position(world, params)?,
99 data: None,
100 },
101 })
102 }
103 }
104}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 8898c12e3..b051c8f6c 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -9,8 +9,9 @@ use std::{
9}; 9};
10 10
11use ide::{ 11use ide::{
12 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, 12 AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex,
13 Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit, 13 NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange,
14 TextEdit,
14}; 15};
15use ide_db::SymbolKind; 16use ide_db::SymbolKind;
16use itertools::Itertools; 17use itertools::Itertools;
@@ -35,7 +36,7 @@ use crate::{
35 cargo_target_spec::CargoTargetSpec, 36 cargo_target_spec::CargoTargetSpec,
36 config::RustfmtConfig, 37 config::RustfmtConfig,
37 diff::diff, 38 diff::diff,
38 from_json, from_proto, 39 from_proto,
39 global_state::{GlobalState, GlobalStateSnapshot}, 40 global_state::{GlobalState, GlobalStateSnapshot},
40 line_endings::LineEndings, 41 line_endings::LineEndings,
41 lsp_ext::{self, InlayHint, InlayHintsParams}, 42 lsp_ext::{self, InlayHint, InlayHintsParams},
@@ -1078,177 +1079,51 @@ pub(crate) fn handle_code_lens(
1078 params: lsp_types::CodeLensParams, 1079 params: lsp_types::CodeLensParams,
1079) -> Result<Option<Vec<CodeLens>>> { 1080) -> Result<Option<Vec<CodeLens>>> {
1080 let _p = profile::span("handle_code_lens"); 1081 let _p = profile::span("handle_code_lens");
1081 let mut lenses: Vec<CodeLens> = Default::default();
1082 1082
1083 let lens_config = snap.config.lens(); 1083 let lens_config = snap.config.lens();
1084 if lens_config.none() { 1084 if lens_config.none() {
1085 // early return before any db query! 1085 // early return before any db query!
1086 return Ok(Some(lenses)); 1086 return Ok(Some(Vec::default()));
1087 } 1087 }
1088 1088
1089 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 1089 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1090 let line_index = snap.analysis.file_line_index(file_id)?; 1090 let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1091 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1092
1093 if lens_config.runnable() {
1094 // Gather runnables
1095 for runnable in snap.analysis.runnables(file_id)? {
1096 if should_skip_target(&runnable, cargo_spec.as_ref()) {
1097 continue;
1098 }
1099
1100 let action = runnable.action();
1101 let range = to_proto::range(&line_index, runnable.nav.full_range);
1102 let r = to_proto::runnable(&snap, file_id, runnable)?;
1103 if lens_config.run {
1104 let lens = CodeLens {
1105 range,
1106 command: Some(run_single_command(&r, action.run_title)),
1107 data: None,
1108 };
1109 lenses.push(lens);
1110 }
1111 1091
1112 if action.debugee && lens_config.debug { 1092 let lenses = snap
1113 let debug_lens = 1093 .analysis
1114 CodeLens { range, command: Some(debug_single_command(&r)), data: None }; 1094 .annotations(
1115 lenses.push(debug_lens); 1095 file_id,
1116 } 1096 AnnotationConfig {
1117 } 1097 binary_target: cargo_target_spec
1118 } 1098 .map(|spec| {
1119 1099 matches!(
1120 if lens_config.implementations || lens_config.refs { 1100 spec.target_kind,
1121 snap.analysis 1101 TargetKind::Bin | TargetKind::Example | TargetKind::Test
1122 .file_structure(file_id)? 1102 )
1123 .into_iter()
1124 .filter(|it| {
1125 matches!(
1126 it.kind,
1127 SymbolKind::Trait | SymbolKind::Struct | SymbolKind::Enum | SymbolKind::Union
1128 )
1129 })
1130 .for_each(|it| {
1131 let range = to_proto::range(&line_index, it.node_range);
1132 let position = to_proto::position(&line_index, it.navigation_range.start());
1133 let doc_pos = lsp_types::TextDocumentPositionParams::new(
1134 params.text_document.clone(),
1135 position,
1136 );
1137 let goto_params = lsp_types::request::GotoImplementationParams {
1138 text_document_position_params: doc_pos.clone(),
1139 work_done_progress_params: Default::default(),
1140 partial_result_params: Default::default(),
1141 };
1142
1143 if lens_config.implementations {
1144 lenses.push(CodeLens {
1145 range,
1146 command: None,
1147 data: Some(to_value(CodeLensResolveData::Impls(goto_params)).unwrap()),
1148 })
1149 }
1150
1151 if lens_config.refs {
1152 lenses.push(CodeLens {
1153 range,
1154 command: None,
1155 data: Some(to_value(CodeLensResolveData::References(doc_pos)).unwrap()),
1156 }) 1103 })
1157 } 1104 .unwrap_or(false),
1158 }); 1105 annotate_runnables: lens_config.runnable(),
1159 } 1106 annotate_impls: lens_config.implementations,
1160 1107 annotate_references: lens_config.refs,
1161 if lens_config.method_refs { 1108 annotate_method_references: lens_config.method_refs,
1162 lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { 1109 run: lens_config.run,
1163 let range = to_proto::range(&line_index, it.range); 1110 debug: lens_config.debug,
1164 let position = to_proto::position(&line_index, it.range.start()); 1111 },
1165 let lens_params = 1112 )?
1166 lsp_types::TextDocumentPositionParams::new(params.text_document.clone(), position); 1113 .into_iter()
1167 1114 .map(|annotation| to_proto::code_lens(&snap, annotation).unwrap())
1168 CodeLens { 1115 .collect();
1169 range,
1170 command: None,
1171 data: Some(to_value(CodeLensResolveData::References(lens_params)).unwrap()),
1172 }
1173 }));
1174 }
1175 1116
1176 Ok(Some(lenses)) 1117 Ok(Some(lenses))
1177} 1118}
1178 1119
1179#[derive(Debug, Serialize, Deserialize)]
1180#[serde(rename_all = "camelCase")]
1181enum CodeLensResolveData {
1182 Impls(lsp_types::request::GotoImplementationParams),
1183 References(lsp_types::TextDocumentPositionParams),
1184}
1185
1186pub(crate) fn handle_code_lens_resolve( 1120pub(crate) fn handle_code_lens_resolve(
1187 snap: GlobalStateSnapshot, 1121 snap: GlobalStateSnapshot,
1188 code_lens: CodeLens, 1122 code_lens: CodeLens,
1189) -> Result<CodeLens> { 1123) -> Result<CodeLens> {
1190 let _p = profile::span("handle_code_lens_resolve"); 1124 let annotation = from_proto::annotation(&snap, code_lens)?;
1191 let data = code_lens.data.unwrap();
1192 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
1193 match resolve {
1194 Some(CodeLensResolveData::Impls(lens_params)) => {
1195 let locations: Vec<Location> =
1196 match handle_goto_implementation(snap, lens_params.clone())? {
1197 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
1198 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
1199 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
1200 .into_iter()
1201 .map(|link| Location::new(link.target_uri, link.target_selection_range))
1202 .collect(),
1203 _ => vec![],
1204 };
1205
1206 let title = implementation_title(locations.len());
1207 let cmd = show_references_command(
1208 title,
1209 &lens_params.text_document_position_params.text_document.uri,
1210 code_lens.range.start,
1211 locations,
1212 );
1213 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
1214 }
1215 Some(CodeLensResolveData::References(doc_position)) => {
1216 let position = from_proto::file_position(&snap, doc_position.clone())?;
1217 let locations = snap
1218 .analysis
1219 .find_all_refs(position, None)
1220 .unwrap_or(None)
1221 .map(|r| {
1222 r.references
1223 .into_iter()
1224 .flat_map(|(file_id, ranges)| {
1225 ranges.into_iter().map(move |(range, _)| FileRange { file_id, range })
1226 })
1227 .filter_map(|frange| to_proto::location(&snap, frange).ok())
1228 .collect_vec()
1229 })
1230 .unwrap_or_default();
1231
1232 let title = reference_title(locations.len());
1233 let cmd = if locations.is_empty() {
1234 Command { title, command: "".into(), arguments: None }
1235 } else {
1236 show_references_command(
1237 title,
1238 &doc_position.text_document.uri,
1239 code_lens.range.start,
1240 locations,
1241 )
1242 };
1243 1125
1244 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) 1126 Ok(to_proto::code_lens(&snap, snap.analysis.resolve_annotation(annotation)?)?)
1245 }
1246 None => Ok(CodeLens {
1247 range: code_lens.range,
1248 command: Some(Command { title: "Error".into(), ..Default::default() }),
1249 data: None,
1250 }),
1251 }
1252} 1127}
1253 1128
1254pub(crate) fn handle_document_highlight( 1129pub(crate) fn handle_document_highlight(
@@ -1547,43 +1422,6 @@ pub(crate) fn handle_open_cargo_toml(
1547 Ok(Some(res)) 1422 Ok(Some(res))
1548} 1423}
1549 1424
1550fn implementation_title(count: usize) -> String {
1551 if count == 1 {
1552 "1 implementation".into()
1553 } else {
1554 format!("{} implementations", count)
1555 }
1556}
1557
1558fn reference_title(count: usize) -> String {
1559 if count == 1 {
1560 "1 reference".into()
1561 } else {
1562 format!("{} references", count)
1563 }
1564}
1565
1566fn show_references_command(
1567 title: String,
1568 uri: &lsp_types::Url,
1569 position: lsp_types::Position,
1570 locations: Vec<lsp_types::Location>,
1571) -> Command {
1572 // We cannot use the 'editor.action.showReferences' command directly
1573 // because that command requires vscode types which we convert in the handler
1574 // on the client side.
1575
1576 Command {
1577 title,
1578 command: "rust-analyzer.showReferences".into(),
1579 arguments: Some(vec![
1580 to_value(uri).unwrap(),
1581 to_value(position).unwrap(),
1582 to_value(locations).unwrap(),
1583 ]),
1584 }
1585}
1586
1587fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { 1425fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1588 Command { 1426 Command {
1589 title: title.to_string(), 1427 title: title.to_string(),
@@ -1635,8 +1473,8 @@ fn show_impl_command_link(
1635 .into_iter() 1473 .into_iter()
1636 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) 1474 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1637 .collect(); 1475 .collect();
1638 let title = implementation_title(locations.len()); 1476 let title = to_proto::implementation_title(locations.len());
1639 let command = show_references_command(title, &uri, position, locations); 1477 let command = to_proto::show_references_command(title, &uri, position, locations);
1640 1478
1641 return Some(lsp_ext::CommandLinkGroup { 1479 return Some(lsp_ext::CommandLinkGroup {
1642 commands: vec![to_command_link(command, "Go to implementations".into())], 1480 commands: vec![to_command_link(command, "Go to implementations".into())],
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index ce5a0e822..a1ad855c3 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -377,3 +377,11 @@ impl Request for OpenCargoToml {
377pub struct OpenCargoTomlParams { 377pub struct OpenCargoTomlParams {
378 pub text_document: TextDocumentIdentifier, 378 pub text_document: TextDocumentIdentifier,
379} 379}
380
381/// Information about CodeLens, that is to be resolved.
382#[derive(Debug, Serialize, Deserialize)]
383#[serde(rename_all = "camelCase")]
384pub(crate) enum CodeLensResolveData {
385 Impls(lsp_types::request::GotoImplementationParams),
386 References(lsp_types::TextDocumentPositionParams),
387}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index be10ac1ae..29fac96fb 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -5,13 +5,15 @@ use std::{
5}; 5};
6 6
7use ide::{ 7use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, 9 Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct,
10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, 10 HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup,
11 RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, 11 NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit,
12 TextRange, TextSize,
12}; 13};
13use ide_db::SymbolKind; 14use ide_db::SymbolKind;
14use itertools::Itertools; 15use itertools::Itertools;
16use serde_json::to_value;
15 17
16use crate::{ 18use crate::{
17 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, 19 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot,
@@ -863,6 +865,141 @@ pub(crate) fn runnable(
863 }) 865 })
864} 866}
865 867
868pub(crate) fn code_lens(
869 snap: &GlobalStateSnapshot,
870 annotation: Annotation,
871) -> Result<lsp_types::CodeLens> {
872 match annotation.kind {
873 AnnotationKind::Runnable { debug, runnable: run } => {
874 let line_index = snap.analysis.file_line_index(run.nav.file_id)?;
875 let annotation_range = range(&line_index, annotation.range);
876
877 let action = run.action();
878 let r = runnable(&snap, run.nav.file_id, run)?;
879
880 let command = if debug {
881 lsp_types::Command {
882 title: action.run_title.to_string(),
883 command: "rust-analyzer.runSingle".into(),
884 arguments: Some(vec![to_value(r).unwrap()]),
885 }
886 } else {
887 lsp_types::Command {
888 title: "Debug".into(),
889 command: "rust-analyzer.debugSingle".into(),
890 arguments: Some(vec![to_value(r).unwrap()]),
891 }
892 };
893
894 Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None })
895 }
896 AnnotationKind::HasImpls { position: file_position, data } => {
897 let line_index = snap.analysis.file_line_index(file_position.file_id)?;
898 let annotation_range = range(&line_index, annotation.range);
899 let url = url(snap, file_position.file_id);
900
901 let position = position(&line_index, file_position.offset);
902
903 let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
904
905 let doc_pos = lsp_types::TextDocumentPositionParams::new(id.clone(), position);
906
907 let goto_params = lsp_types::request::GotoImplementationParams {
908 text_document_position_params: doc_pos.clone(),
909 work_done_progress_params: Default::default(),
910 partial_result_params: Default::default(),
911 };
912
913 let command = data.map(|ranges| {
914 let locations: Vec<lsp_types::Location> = ranges
915 .into_iter()
916 .filter_map(|target| {
917 location(
918 snap,
919 FileRange { file_id: target.file_id, range: target.full_range },
920 )
921 .ok()
922 })
923 .collect();
924
925 show_references_command(
926 implementation_title(locations.len()),
927 &url,
928 position,
929 locations,
930 )
931 });
932
933 Ok(lsp_types::CodeLens {
934 range: annotation_range,
935 command,
936 data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
937 })
938 }
939 AnnotationKind::HasReferences { position: file_position, data } => {
940 let line_index = snap.analysis.file_line_index(file_position.file_id)?;
941 let annotation_range = range(&line_index, annotation.range);
942 let url = url(snap, file_position.file_id);
943
944 let position = position(&line_index, file_position.offset);
945
946 let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
947
948 let doc_pos = lsp_types::TextDocumentPositionParams::new(id, position);
949
950 let command = data.map(|ranges| {
951 let locations: Vec<lsp_types::Location> =
952 ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
953
954 show_references_command(reference_title(locations.len()), &url, position, locations)
955 });
956
957 Ok(lsp_types::CodeLens {
958 range: annotation_range,
959 command,
960 data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
961 })
962 }
963 }
964}
965
966pub(crate) fn show_references_command(
967 title: String,
968 uri: &lsp_types::Url,
969 position: lsp_types::Position,
970 locations: Vec<lsp_types::Location>,
971) -> lsp_types::Command {
972 // We cannot use the 'editor.action.showReferences' command directly
973 // because that command requires vscode types which we convert in the handler
974 // on the client side.
975
976 lsp_types::Command {
977 title,
978 command: "rust-analyzer.showReferences".into(),
979 arguments: Some(vec![
980 to_value(uri).unwrap(),
981 to_value(position).unwrap(),
982 to_value(locations).unwrap(),
983 ]),
984 }
985}
986
987pub(crate) fn implementation_title(count: usize) -> String {
988 if count == 1 {
989 "1 implementation".into()
990 } else {
991 format!("{} implementations", count)
992 }
993}
994
995pub(crate) fn reference_title(count: usize) -> String {
996 if count == 1 {
997 "1 reference".into()
998 } else {
999 format!("{} references", count)
1000 }
1001}
1002
866pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { 1003pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
867 let value = crate::markdown::format_docs(markup.as_str()); 1004 let value = crate::markdown::format_docs(markup.as_str());
868 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } 1005 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }