aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--docs/dev/lsp-extensions.md13
7 files changed, 388 insertions, 202 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 }
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 2a966a96d..51aa79517 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
1<!--- 1<!---
2lsp_ext.rs hash: 8f1ae8530f69e3a3 2lsp_ext.rs hash: 34aec6bfeaeb97a
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need 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
573``` 573```
574 574
575`experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword. 575`experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
576
577## CodeLens resolve request
578
579This request is sent from client to server to resolve previously provided CodeLens.
580
581As an alternative to `any` type in `data` field of `CodeLens`, you may use `CodeLensResolveData`:
582```typescript
583interface CodeLensResolveData {
584 data: (DefinitionParams | TextDocumentPositionParams),
585}
586```