diff options
-rw-r--r-- | crates/ide/src/annotations.rs | 142 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/from_proto.rs | 40 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 228 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 145 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 13 |
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 @@ | |||
1 | use hir::Semantics; | ||
2 | use ide_db::{ | ||
3 | base_db::{FileId, FilePosition, FileRange, SourceDatabase}, | ||
4 | RootDatabase, SymbolKind, | ||
5 | }; | ||
6 | use syntax::TextRange; | ||
7 | |||
8 | use 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. | ||
21 | pub struct Annotation { | ||
22 | pub range: TextRange, | ||
23 | pub kind: AnnotationKind, | ||
24 | } | ||
25 | |||
26 | pub 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 | |||
32 | pub 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 | |||
42 | pub(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 | |||
122 | pub(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; | |||
22 | mod prime_caches; | 22 | mod prime_caches; |
23 | mod display; | 23 | mod display; |
24 | 24 | ||
25 | mod annotations; | ||
25 | mod call_hierarchy; | 26 | mod call_hierarchy; |
26 | mod diagnostics; | 27 | mod diagnostics; |
27 | mod expand_macro; | 28 | mod expand_macro; |
@@ -63,6 +64,7 @@ use syntax::SourceFile; | |||
63 | use crate::display::ToNav; | 64 | use crate::display::ToNav; |
64 | 65 | ||
65 | pub use crate::{ | 66 | pub 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. |
2 | use std::convert::TryFrom; | 2 | use std::convert::TryFrom; |
3 | 3 | ||
4 | use ide::{AssistKind, LineCol, LineIndex}; | 4 | use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex}; |
5 | use ide_db::base_db::{FileId, FilePosition, FileRange}; | 5 | use ide_db::base_db::{FileId, FilePosition, FileRange}; |
6 | use syntax::{TextRange, TextSize}; | 6 | use syntax::{TextRange, TextSize}; |
7 | use vfs::AbsPathBuf; | 7 | use vfs::AbsPathBuf; |
8 | 8 | ||
9 | use crate::{global_state::GlobalStateSnapshot, Result}; | 9 | use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result}; |
10 | 10 | ||
11 | pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> { | 11 | pub(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 | |||
70 | pub(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(¶ms.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(¶ms.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 | ||
11 | use ide::{ | 11 | use 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 | }; |
15 | use ide_db::SymbolKind; | 16 | use ide_db::SymbolKind; |
16 | use itertools::Itertools; | 17 | use 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, ¶ms.text_document.uri)?; | 1089 | let file_id = from_proto::file_id(&snap, ¶ms.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")] | ||
1181 | enum CodeLensResolveData { | ||
1182 | Impls(lsp_types::request::GotoImplementationParams), | ||
1183 | References(lsp_types::TextDocumentPositionParams), | ||
1184 | } | ||
1185 | |||
1186 | pub(crate) fn handle_code_lens_resolve( | 1120 | pub(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 | ||
1254 | pub(crate) fn handle_document_highlight( | 1129 | pub(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 | ||
1550 | fn implementation_title(count: usize) -> String { | ||
1551 | if count == 1 { | ||
1552 | "1 implementation".into() | ||
1553 | } else { | ||
1554 | format!("{} implementations", count) | ||
1555 | } | ||
1556 | } | ||
1557 | |||
1558 | fn reference_title(count: usize) -> String { | ||
1559 | if count == 1 { | ||
1560 | "1 reference".into() | ||
1561 | } else { | ||
1562 | format!("{} references", count) | ||
1563 | } | ||
1564 | } | ||
1565 | |||
1566 | fn 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 | |||
1587 | fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { | 1425 | fn 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 { | |||
377 | pub struct OpenCargoTomlParams { | 377 | pub 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")] | ||
384 | pub(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 | ||
7 | use ide::{ | 7 | use 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 | }; |
13 | use ide_db::SymbolKind; | 14 | use ide_db::SymbolKind; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
16 | use serde_json::to_value; | ||
15 | 17 | ||
16 | use crate::{ | 18 | use 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 | ||
868 | pub(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 | |||
966 | pub(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 | |||
987 | pub(crate) fn implementation_title(count: usize) -> String { | ||
988 | if count == 1 { | ||
989 | "1 implementation".into() | ||
990 | } else { | ||
991 | format!("{} implementations", count) | ||
992 | } | ||
993 | } | ||
994 | |||
995 | pub(crate) fn reference_title(count: usize) -> String { | ||
996 | if count == 1 { | ||
997 | "1 reference".into() | ||
998 | } else { | ||
999 | format!("{} references", count) | ||
1000 | } | ||
1001 | } | ||
1002 | |||
866 | pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { | 1003 | pub(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 | <!--- |
2 | lsp_ext.rs hash: 8f1ae8530f69e3a3 | 2 | lsp_ext.rs hash: 34aec6bfeaeb97a |
3 | 3 | ||
4 | If you need to change the above hash to make the test pass, please check if you | 4 | If you need to change the above hash to make the test pass, please check if you |
5 | need to adjust this doc as well and ping this issue: | 5 | 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 | |||
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 | |||
579 | This request is sent from client to server to resolve previously provided CodeLens. | ||
580 | |||
581 | As an alternative to `any` type in `data` field of `CodeLens`, you may use `CodeLensResolveData`: | ||
582 | ```typescript | ||
583 | interface CodeLensResolveData { | ||
584 | data: (DefinitionParams | TextDocumentPositionParams), | ||
585 | } | ||
586 | ``` | ||