diff options
Diffstat (limited to 'crates/rust-analyzer/src/main_loop/handlers.rs')
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 800 |
1 files changed, 396 insertions, 404 deletions
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 0f623949e..121964718 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -11,17 +11,18 @@ use lsp_server::ErrorCode; | |||
11 | use lsp_types::{ | 11 | use lsp_types::{ |
12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, | 12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, |
13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
14 | CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, | 14 | CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, |
15 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, | 15 | DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, |
16 | Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, | 16 | MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, |
17 | Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, | 17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, |
18 | SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | TextEdit, Url, WorkspaceEdit, | ||
20 | }; | 19 | }; |
21 | use ra_ide::{ | 20 | use ra_ide::{ |
22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
22 | TextEdit, | ||
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | ||
25 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; |
26 | use rustc_hash::FxHashMap; | 27 | use rustc_hash::FxHashMap; |
27 | use serde::{Deserialize, Serialize}; | 28 | use serde::{Deserialize, Serialize}; |
@@ -31,14 +32,10 @@ use stdx::format_to; | |||
31 | use crate::{ | 32 | use crate::{ |
32 | cargo_target_spec::CargoTargetSpec, | 33 | cargo_target_spec::CargoTargetSpec, |
33 | config::RustfmtConfig, | 34 | config::RustfmtConfig, |
34 | conv::{ | ||
35 | to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, | ||
36 | TryConvWithToVec, | ||
37 | }, | ||
38 | diagnostics::DiagnosticTask, | 35 | diagnostics::DiagnosticTask, |
39 | from_json, | 36 | from_json, from_proto, |
40 | req::{self, InlayHint, InlayHintsParams}, | 37 | lsp_ext::{self, InlayHint, InlayHintsParams}, |
41 | semantic_tokens::SemanticTokensBuilder, | 38 | to_proto, |
42 | world::WorldSnapshot, | 39 | world::WorldSnapshot, |
43 | LspError, Result, | 40 | LspError, Result, |
44 | }; | 41 | }; |
@@ -46,57 +43,60 @@ use crate::{ | |||
46 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 43 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { |
47 | let _p = profile("handle_analyzer_status"); | 44 | let _p = profile("handle_analyzer_status"); |
48 | let mut buf = world.status(); | 45 | let mut buf = world.status(); |
49 | format_to!(buf, "\n\nrequests:"); | 46 | format_to!(buf, "\n\nrequests:\n"); |
50 | let requests = world.latest_requests.read(); | 47 | let requests = world.latest_requests.read(); |
51 | for (is_last, r) in requests.iter() { | 48 | for (is_last, r) in requests.iter() { |
52 | let mark = if is_last { "*" } else { " " }; | 49 | let mark = if is_last { "*" } else { " " }; |
53 | format_to!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis()); | 50 | format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); |
54 | } | 51 | } |
55 | Ok(buf) | 52 | Ok(buf) |
56 | } | 53 | } |
57 | 54 | ||
58 | pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> { | 55 | pub fn handle_syntax_tree( |
56 | world: WorldSnapshot, | ||
57 | params: lsp_ext::SyntaxTreeParams, | ||
58 | ) -> Result<String> { | ||
59 | let _p = profile("handle_syntax_tree"); | 59 | let _p = profile("handle_syntax_tree"); |
60 | let id = params.text_document.try_conv_with(&world)?; | 60 | let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
61 | let line_index = world.analysis().file_line_index(id)?; | 61 | let line_index = world.analysis().file_line_index(id)?; |
62 | let text_range = params.range.map(|p| p.conv_with(&line_index)); | 62 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); |
63 | let res = world.analysis().syntax_tree(id, text_range)?; | 63 | let res = world.analysis().syntax_tree(id, text_range)?; |
64 | Ok(res) | 64 | Ok(res) |
65 | } | 65 | } |
66 | 66 | ||
67 | pub fn handle_expand_macro( | 67 | pub fn handle_expand_macro( |
68 | world: WorldSnapshot, | 68 | world: WorldSnapshot, |
69 | params: req::ExpandMacroParams, | 69 | params: lsp_ext::ExpandMacroParams, |
70 | ) -> Result<Option<req::ExpandedMacro>> { | 70 | ) -> Result<Option<lsp_ext::ExpandedMacro>> { |
71 | let _p = profile("handle_expand_macro"); | 71 | let _p = profile("handle_expand_macro"); |
72 | let file_id = params.text_document.try_conv_with(&world)?; | 72 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
73 | let line_index = world.analysis().file_line_index(file_id)?; | 73 | let line_index = world.analysis().file_line_index(file_id)?; |
74 | let offset = params.position.map(|p| p.conv_with(&line_index)); | 74 | let offset = params.position.map(|p| from_proto::offset(&line_index, p)); |
75 | 75 | ||
76 | match offset { | 76 | match offset { |
77 | None => Ok(None), | 77 | None => Ok(None), |
78 | Some(offset) => { | 78 | Some(offset) => { |
79 | let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; | 79 | let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; |
80 | Ok(res.map(|it| req::ExpandedMacro { name: it.name, expansion: it.expansion })) | 80 | Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) |
81 | } | 81 | } |
82 | } | 82 | } |
83 | } | 83 | } |
84 | 84 | ||
85 | pub fn handle_selection_range( | 85 | pub fn handle_selection_range( |
86 | world: WorldSnapshot, | 86 | world: WorldSnapshot, |
87 | params: req::SelectionRangeParams, | 87 | params: lsp_types::SelectionRangeParams, |
88 | ) -> Result<Option<Vec<req::SelectionRange>>> { | 88 | ) -> Result<Option<Vec<lsp_types::SelectionRange>>> { |
89 | let _p = profile("handle_selection_range"); | 89 | let _p = profile("handle_selection_range"); |
90 | let file_id = params.text_document.try_conv_with(&world)?; | 90 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
91 | let line_index = world.analysis().file_line_index(file_id)?; | 91 | let line_index = world.analysis().file_line_index(file_id)?; |
92 | let res: Result<Vec<req::SelectionRange>> = params | 92 | let res: Result<Vec<lsp_types::SelectionRange>> = params |
93 | .positions | 93 | .positions |
94 | .into_iter() | 94 | .into_iter() |
95 | .map_conv_with(&line_index) | ||
96 | .map(|position| { | 95 | .map(|position| { |
96 | let offset = from_proto::offset(&line_index, position); | ||
97 | let mut ranges = Vec::new(); | 97 | let mut ranges = Vec::new(); |
98 | { | 98 | { |
99 | let mut range = TextRange::new(position, position); | 99 | let mut range = TextRange::new(offset, offset); |
100 | loop { | 100 | loop { |
101 | ranges.push(range); | 101 | ranges.push(range); |
102 | let frange = FileRange { file_id, range }; | 102 | let frange = FileRange { file_id, range }; |
@@ -108,13 +108,13 @@ pub fn handle_selection_range( | |||
108 | } | 108 | } |
109 | } | 109 | } |
110 | } | 110 | } |
111 | let mut range = req::SelectionRange { | 111 | let mut range = lsp_types::SelectionRange { |
112 | range: ranges.last().unwrap().conv_with(&line_index), | 112 | range: to_proto::range(&line_index, *ranges.last().unwrap()), |
113 | parent: None, | 113 | parent: None, |
114 | }; | 114 | }; |
115 | for r in ranges.iter().rev().skip(1) { | 115 | for &r in ranges.iter().rev().skip(1) { |
116 | range = req::SelectionRange { | 116 | range = lsp_types::SelectionRange { |
117 | range: r.conv_with(&line_index), | 117 | range: to_proto::range(&line_index, r), |
118 | parent: Some(Box::new(range)), | 118 | parent: Some(Box::new(range)), |
119 | } | 119 | } |
120 | } | 120 | } |
@@ -127,57 +127,68 @@ pub fn handle_selection_range( | |||
127 | 127 | ||
128 | pub fn handle_find_matching_brace( | 128 | pub fn handle_find_matching_brace( |
129 | world: WorldSnapshot, | 129 | world: WorldSnapshot, |
130 | params: req::FindMatchingBraceParams, | 130 | params: lsp_ext::FindMatchingBraceParams, |
131 | ) -> Result<Vec<Position>> { | 131 | ) -> Result<Vec<Position>> { |
132 | let _p = profile("handle_find_matching_brace"); | 132 | let _p = profile("handle_find_matching_brace"); |
133 | let file_id = params.text_document.try_conv_with(&world)?; | 133 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
134 | let line_index = world.analysis().file_line_index(file_id)?; | 134 | let line_index = world.analysis().file_line_index(file_id)?; |
135 | let res = params | 135 | let res = params |
136 | .offsets | 136 | .offsets |
137 | .into_iter() | 137 | .into_iter() |
138 | .map_conv_with(&line_index) | 138 | .map(|position| { |
139 | .map(|offset| { | 139 | let offset = from_proto::offset(&line_index, position); |
140 | if let Ok(Some(matching_brace_offset)) = | 140 | let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { |
141 | world.analysis().matching_brace(FilePosition { file_id, offset }) | 141 | Ok(Some(matching_brace_offset)) => matching_brace_offset, |
142 | { | 142 | Err(_) | Ok(None) => offset, |
143 | matching_brace_offset | 143 | }; |
144 | } else { | 144 | to_proto::position(&line_index, offset) |
145 | offset | ||
146 | } | ||
147 | }) | 145 | }) |
148 | .map_conv_with(&line_index) | ||
149 | .collect(); | 146 | .collect(); |
150 | Ok(res) | 147 | Ok(res) |
151 | } | 148 | } |
152 | 149 | ||
153 | pub fn handle_join_lines( | 150 | pub fn handle_join_lines( |
154 | world: WorldSnapshot, | 151 | world: WorldSnapshot, |
155 | params: req::JoinLinesParams, | 152 | params: lsp_ext::JoinLinesParams, |
156 | ) -> Result<req::SourceChange> { | 153 | ) -> Result<Vec<lsp_types::TextEdit>> { |
157 | let _p = profile("handle_join_lines"); | 154 | let _p = profile("handle_join_lines"); |
158 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | 155 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
159 | world.analysis().join_lines(frange)?.try_conv_with(&world) | 156 | let line_index = world.analysis().file_line_index(file_id)?; |
157 | let line_endings = world.file_line_endings(file_id); | ||
158 | let mut res = TextEdit::default(); | ||
159 | for range in params.ranges { | ||
160 | let range = from_proto::text_range(&line_index, range); | ||
161 | let edit = world.analysis().join_lines(FileRange { file_id, range })?; | ||
162 | match res.union(edit) { | ||
163 | Ok(()) => (), | ||
164 | Err(_edit) => { | ||
165 | // just ignore overlapping edits | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | let res = to_proto::text_edit_vec(&line_index, line_endings, res); | ||
170 | Ok(res) | ||
160 | } | 171 | } |
161 | 172 | ||
162 | pub fn handle_on_enter( | 173 | pub fn handle_on_enter( |
163 | world: WorldSnapshot, | 174 | world: WorldSnapshot, |
164 | params: req::TextDocumentPositionParams, | 175 | params: lsp_types::TextDocumentPositionParams, |
165 | ) -> Result<Option<req::SourceChange>> { | 176 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { |
166 | let _p = profile("handle_on_enter"); | 177 | let _p = profile("handle_on_enter"); |
167 | let position = params.try_conv_with(&world)?; | 178 | let position = from_proto::file_position(&world, params)?; |
168 | match world.analysis().on_enter(position)? { | 179 | match world.analysis().on_enter(position)? { |
169 | None => Ok(None), | 180 | None => Ok(None), |
170 | Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), | 181 | Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some), |
171 | } | 182 | } |
172 | } | 183 | } |
173 | 184 | ||
174 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. | 185 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. |
175 | pub fn handle_on_type_formatting( | 186 | pub fn handle_on_type_formatting( |
176 | world: WorldSnapshot, | 187 | world: WorldSnapshot, |
177 | params: req::DocumentOnTypeFormattingParams, | 188 | params: lsp_types::DocumentOnTypeFormattingParams, |
178 | ) -> Result<Option<Vec<TextEdit>>> { | 189 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
179 | let _p = profile("handle_on_type_formatting"); | 190 | let _p = profile("handle_on_type_formatting"); |
180 | let mut position = params.text_document_position.try_conv_with(&world)?; | 191 | let mut position = from_proto::file_position(&world, params.text_document_position)?; |
181 | let line_index = world.analysis().file_line_index(position.file_id)?; | 192 | let line_index = world.analysis().file_line_index(position.file_id)?; |
182 | let line_endings = world.file_line_endings(position.file_id); | 193 | let line_endings = world.file_line_endings(position.file_id); |
183 | 194 | ||
@@ -207,18 +218,17 @@ pub fn handle_on_type_formatting( | |||
207 | // This should be a single-file edit | 218 | // This should be a single-file edit |
208 | let edit = edit.source_file_edits.pop().unwrap(); | 219 | let edit = edit.source_file_edits.pop().unwrap(); |
209 | 220 | ||
210 | let change: Vec<TextEdit> = edit.edit.conv_with((&line_index, line_endings)); | 221 | let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit); |
211 | Ok(Some(change)) | 222 | Ok(Some(change)) |
212 | } | 223 | } |
213 | 224 | ||
214 | pub fn handle_document_symbol( | 225 | pub fn handle_document_symbol( |
215 | world: WorldSnapshot, | 226 | world: WorldSnapshot, |
216 | params: req::DocumentSymbolParams, | 227 | params: lsp_types::DocumentSymbolParams, |
217 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 228 | ) -> Result<Option<lsp_types::DocumentSymbolResponse>> { |
218 | let _p = profile("handle_document_symbol"); | 229 | let _p = profile("handle_document_symbol"); |
219 | let file_id = params.text_document.try_conv_with(&world)?; | 230 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
220 | let line_index = world.analysis().file_line_index(file_id)?; | 231 | let line_index = world.analysis().file_line_index(file_id)?; |
221 | let url = file_id.try_conv_with(&world)?; | ||
222 | 232 | ||
223 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 233 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
224 | 234 | ||
@@ -226,10 +236,10 @@ pub fn handle_document_symbol( | |||
226 | let doc_symbol = DocumentSymbol { | 236 | let doc_symbol = DocumentSymbol { |
227 | name: symbol.label, | 237 | name: symbol.label, |
228 | detail: symbol.detail, | 238 | detail: symbol.detail, |
229 | kind: symbol.kind.conv(), | 239 | kind: to_proto::symbol_kind(symbol.kind), |
230 | deprecated: Some(symbol.deprecated), | 240 | deprecated: Some(symbol.deprecated), |
231 | range: symbol.node_range.conv_with(&line_index), | 241 | range: to_proto::range(&line_index, symbol.node_range), |
232 | selection_range: symbol.navigation_range.conv_with(&line_index), | 242 | selection_range: to_proto::range(&line_index, symbol.navigation_range), |
233 | children: None, | 243 | children: None, |
234 | }; | 244 | }; |
235 | parents.push((doc_symbol, symbol.parent)); | 245 | parents.push((doc_symbol, symbol.parent)); |
@@ -248,40 +258,41 @@ pub fn handle_document_symbol( | |||
248 | } | 258 | } |
249 | } | 259 | } |
250 | 260 | ||
251 | if world.config.client_caps.hierarchical_symbols { | 261 | let res = if world.config.client_caps.hierarchical_symbols { |
252 | Ok(Some(document_symbols.into())) | 262 | document_symbols.into() |
253 | } else { | 263 | } else { |
264 | let url = to_proto::url(&world, file_id)?; | ||
254 | let mut symbol_information = Vec::<SymbolInformation>::new(); | 265 | let mut symbol_information = Vec::<SymbolInformation>::new(); |
255 | for symbol in document_symbols { | 266 | for symbol in document_symbols { |
256 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); | 267 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); |
257 | } | 268 | } |
269 | symbol_information.into() | ||
270 | }; | ||
271 | return Ok(Some(res)); | ||
258 | 272 | ||
259 | Ok(Some(symbol_information.into())) | 273 | fn flatten_document_symbol( |
260 | } | 274 | symbol: &DocumentSymbol, |
261 | } | 275 | container_name: Option<String>, |
262 | 276 | url: &Url, | |
263 | fn flatten_document_symbol( | 277 | res: &mut Vec<SymbolInformation>, |
264 | symbol: &DocumentSymbol, | 278 | ) { |
265 | container_name: Option<String>, | 279 | res.push(SymbolInformation { |
266 | url: &Url, | 280 | name: symbol.name.clone(), |
267 | res: &mut Vec<SymbolInformation>, | 281 | kind: symbol.kind, |
268 | ) { | 282 | deprecated: symbol.deprecated, |
269 | res.push(SymbolInformation { | 283 | location: Location::new(url.clone(), symbol.range), |
270 | name: symbol.name.clone(), | 284 | container_name: container_name, |
271 | kind: symbol.kind, | 285 | }); |
272 | deprecated: symbol.deprecated, | ||
273 | location: Location::new(url.clone(), symbol.range), | ||
274 | container_name: container_name, | ||
275 | }); | ||
276 | 286 | ||
277 | for child in symbol.children.iter().flatten() { | 287 | for child in symbol.children.iter().flatten() { |
278 | flatten_document_symbol(child, Some(symbol.name.clone()), url, res); | 288 | flatten_document_symbol(child, Some(symbol.name.clone()), url, res); |
289 | } | ||
279 | } | 290 | } |
280 | } | 291 | } |
281 | 292 | ||
282 | pub fn handle_workspace_symbol( | 293 | pub fn handle_workspace_symbol( |
283 | world: WorldSnapshot, | 294 | world: WorldSnapshot, |
284 | params: req::WorkspaceSymbolParams, | 295 | params: lsp_types::WorkspaceSymbolParams, |
285 | ) -> Result<Option<Vec<SymbolInformation>>> { | 296 | ) -> Result<Option<Vec<SymbolInformation>>> { |
286 | let _p = profile("handle_workspace_symbol"); | 297 | let _p = profile("handle_workspace_symbol"); |
287 | let all_symbols = params.query.contains('#'); | 298 | let all_symbols = params.query.contains('#'); |
@@ -312,8 +323,8 @@ pub fn handle_workspace_symbol( | |||
312 | for nav in world.analysis().symbol_search(query)? { | 323 | for nav in world.analysis().symbol_search(query)? { |
313 | let info = SymbolInformation { | 324 | let info = SymbolInformation { |
314 | name: nav.name().to_string(), | 325 | name: nav.name().to_string(), |
315 | kind: nav.kind().conv(), | 326 | kind: to_proto::symbol_kind(nav.kind()), |
316 | location: nav.try_conv_with(world)?, | 327 | location: to_proto::location(world, nav.file_range())?, |
317 | container_name: nav.container_name().map(|v| v.to_string()), | 328 | container_name: nav.container_name().map(|v| v.to_string()), |
318 | deprecated: None, | 329 | deprecated: None, |
319 | }; | 330 | }; |
@@ -325,86 +336,110 @@ pub fn handle_workspace_symbol( | |||
325 | 336 | ||
326 | pub fn handle_goto_definition( | 337 | pub fn handle_goto_definition( |
327 | world: WorldSnapshot, | 338 | world: WorldSnapshot, |
328 | params: req::GotoDefinitionParams, | 339 | params: lsp_types::GotoDefinitionParams, |
329 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 340 | ) -> Result<Option<lsp_types::GotoDefinitionResponse>> { |
330 | let _p = profile("handle_goto_definition"); | 341 | let _p = profile("handle_goto_definition"); |
331 | let position = params.text_document_position_params.try_conv_with(&world)?; | 342 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
332 | let nav_info = match world.analysis().goto_definition(position)? { | 343 | let nav_info = match world.analysis().goto_definition(position)? { |
333 | None => return Ok(None), | 344 | None => return Ok(None), |
334 | Some(it) => it, | 345 | Some(it) => it, |
335 | }; | 346 | }; |
336 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 347 | let res = to_proto::goto_definition_response( |
348 | &world, | ||
349 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
350 | nav_info.info, | ||
351 | )?; | ||
337 | Ok(Some(res)) | 352 | Ok(Some(res)) |
338 | } | 353 | } |
339 | 354 | ||
340 | pub fn handle_goto_implementation( | 355 | pub fn handle_goto_implementation( |
341 | world: WorldSnapshot, | 356 | world: WorldSnapshot, |
342 | params: req::GotoImplementationParams, | 357 | params: lsp_types::request::GotoImplementationParams, |
343 | ) -> Result<Option<req::GotoImplementationResponse>> { | 358 | ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { |
344 | let _p = profile("handle_goto_implementation"); | 359 | let _p = profile("handle_goto_implementation"); |
345 | let position = params.text_document_position_params.try_conv_with(&world)?; | 360 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
346 | let nav_info = match world.analysis().goto_implementation(position)? { | 361 | let nav_info = match world.analysis().goto_implementation(position)? { |
347 | None => return Ok(None), | 362 | None => return Ok(None), |
348 | Some(it) => it, | 363 | Some(it) => it, |
349 | }; | 364 | }; |
350 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 365 | let res = to_proto::goto_definition_response( |
366 | &world, | ||
367 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
368 | nav_info.info, | ||
369 | )?; | ||
351 | Ok(Some(res)) | 370 | Ok(Some(res)) |
352 | } | 371 | } |
353 | 372 | ||
354 | pub fn handle_goto_type_definition( | 373 | pub fn handle_goto_type_definition( |
355 | world: WorldSnapshot, | 374 | world: WorldSnapshot, |
356 | params: req::GotoTypeDefinitionParams, | 375 | params: lsp_types::request::GotoTypeDefinitionParams, |
357 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { | 376 | ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { |
358 | let _p = profile("handle_goto_type_definition"); | 377 | let _p = profile("handle_goto_type_definition"); |
359 | let position = params.text_document_position_params.try_conv_with(&world)?; | 378 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
360 | let nav_info = match world.analysis().goto_type_definition(position)? { | 379 | let nav_info = match world.analysis().goto_type_definition(position)? { |
361 | None => return Ok(None), | 380 | None => return Ok(None), |
362 | Some(it) => it, | 381 | Some(it) => it, |
363 | }; | 382 | }; |
364 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 383 | let res = to_proto::goto_definition_response( |
384 | &world, | ||
385 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
386 | nav_info.info, | ||
387 | )?; | ||
365 | Ok(Some(res)) | 388 | Ok(Some(res)) |
366 | } | 389 | } |
367 | 390 | ||
368 | pub fn handle_parent_module( | 391 | pub fn handle_parent_module( |
369 | world: WorldSnapshot, | 392 | world: WorldSnapshot, |
370 | params: req::TextDocumentPositionParams, | 393 | params: lsp_types::TextDocumentPositionParams, |
371 | ) -> Result<Vec<Location>> { | 394 | ) -> Result<Vec<Location>> { |
372 | let _p = profile("handle_parent_module"); | 395 | let _p = profile("handle_parent_module"); |
373 | let position = params.try_conv_with(&world)?; | 396 | let position = from_proto::file_position(&world, params)?; |
374 | world.analysis().parent_module(position)?.iter().try_conv_with_to_vec(&world) | 397 | world |
398 | .analysis() | ||
399 | .parent_module(position)? | ||
400 | .into_iter() | ||
401 | .map(|it| to_proto::location(&world, it.file_range())) | ||
402 | .collect::<Result<Vec<_>>>() | ||
375 | } | 403 | } |
376 | 404 | ||
377 | pub fn handle_runnables( | 405 | pub fn handle_runnables( |
378 | world: WorldSnapshot, | 406 | world: WorldSnapshot, |
379 | params: req::RunnablesParams, | 407 | params: lsp_ext::RunnablesParams, |
380 | ) -> Result<Vec<req::Runnable>> { | 408 | ) -> Result<Vec<lsp_ext::Runnable>> { |
381 | let _p = profile("handle_runnables"); | 409 | let _p = profile("handle_runnables"); |
382 | let file_id = params.text_document.try_conv_with(&world)?; | 410 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
383 | let line_index = world.analysis().file_line_index(file_id)?; | 411 | let line_index = world.analysis().file_line_index(file_id)?; |
384 | let offset = params.position.map(|it| it.conv_with(&line_index)); | 412 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
385 | let mut res = Vec::new(); | 413 | let mut res = Vec::new(); |
386 | let workspace_root = world.workspace_root_for(file_id); | 414 | let workspace_root = world.workspace_root_for(file_id); |
415 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; | ||
387 | for runnable in world.analysis().runnables(file_id)? { | 416 | for runnable in world.analysis().runnables(file_id)? { |
388 | if let Some(offset) = offset { | 417 | if let Some(offset) = offset { |
389 | if !runnable.range.contains_inclusive(offset) { | 418 | if !runnable.range.contains_inclusive(offset) { |
390 | continue; | 419 | continue; |
391 | } | 420 | } |
392 | } | 421 | } |
422 | // Do not suggest binary run on other target than binary | ||
423 | if let RunnableKind::Bin = runnable.kind { | ||
424 | if let Some(spec) = &cargo_spec { | ||
425 | match spec.target_kind { | ||
426 | TargetKind::Bin => {} | ||
427 | _ => continue, | ||
428 | } | ||
429 | } | ||
430 | } | ||
393 | res.push(to_lsp_runnable(&world, file_id, runnable)?); | 431 | res.push(to_lsp_runnable(&world, file_id, runnable)?); |
394 | } | 432 | } |
433 | |||
395 | // Add `cargo check` and `cargo test` for the whole package | 434 | // Add `cargo check` and `cargo test` for the whole package |
396 | match CargoTargetSpec::for_file(&world, file_id)? { | 435 | match cargo_spec { |
397 | Some(spec) => { | 436 | Some(spec) => { |
398 | for &cmd in ["check", "test"].iter() { | 437 | for &cmd in ["check", "test"].iter() { |
399 | res.push(req::Runnable { | 438 | res.push(lsp_ext::Runnable { |
400 | range: Default::default(), | 439 | range: Default::default(), |
401 | label: format!("cargo {} -p {}", cmd, spec.package), | 440 | label: format!("cargo {} -p {}", cmd, spec.package), |
402 | bin: "cargo".to_string(), | 441 | bin: "cargo".to_string(), |
403 | args: { | 442 | args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], |
404 | let mut args = vec![cmd.to_string()]; | ||
405 | spec.clone().push_to(&mut args); | ||
406 | args | ||
407 | }, | ||
408 | extra_args: Vec::new(), | 443 | extra_args: Vec::new(), |
409 | env: FxHashMap::default(), | 444 | env: FxHashMap::default(), |
410 | cwd: workspace_root.map(|root| root.to_owned()), | 445 | cwd: workspace_root.map(|root| root.to_owned()), |
@@ -412,7 +447,7 @@ pub fn handle_runnables( | |||
412 | } | 447 | } |
413 | } | 448 | } |
414 | None => { | 449 | None => { |
415 | res.push(req::Runnable { | 450 | res.push(lsp_ext::Runnable { |
416 | range: Default::default(), | 451 | range: Default::default(), |
417 | label: "cargo check --workspace".to_string(), | 452 | label: "cargo check --workspace".to_string(), |
418 | bin: "cargo".to_string(), | 453 | bin: "cargo".to_string(), |
@@ -428,10 +463,10 @@ pub fn handle_runnables( | |||
428 | 463 | ||
429 | pub fn handle_completion( | 464 | pub fn handle_completion( |
430 | world: WorldSnapshot, | 465 | world: WorldSnapshot, |
431 | params: req::CompletionParams, | 466 | params: lsp_types::CompletionParams, |
432 | ) -> Result<Option<req::CompletionResponse>> { | 467 | ) -> Result<Option<lsp_types::CompletionResponse>> { |
433 | let _p = profile("handle_completion"); | 468 | let _p = profile("handle_completion"); |
434 | let position = params.text_document_position.try_conv_with(&world)?; | 469 | let position = from_proto::file_position(&world, params.text_document_position)?; |
435 | let completion_triggered_after_single_colon = { | 470 | let completion_triggered_after_single_colon = { |
436 | let mut res = false; | 471 | let mut res = false; |
437 | if let Some(ctx) = params.context { | 472 | if let Some(ctx) = params.context { |
@@ -454,14 +489,16 @@ pub fn handle_completion( | |||
454 | return Ok(None); | 489 | return Ok(None); |
455 | } | 490 | } |
456 | 491 | ||
457 | let items = match world.analysis().completions(position, &world.config.completion)? { | 492 | let items = match world.analysis().completions(&world.config.completion, position)? { |
458 | None => return Ok(None), | 493 | None => return Ok(None), |
459 | Some(items) => items, | 494 | Some(items) => items, |
460 | }; | 495 | }; |
461 | let line_index = world.analysis().file_line_index(position.file_id)?; | 496 | let line_index = world.analysis().file_line_index(position.file_id)?; |
462 | let line_endings = world.file_line_endings(position.file_id); | 497 | let line_endings = world.file_line_endings(position.file_id); |
463 | let items: Vec<CompletionItem> = | 498 | let items: Vec<CompletionItem> = items |
464 | items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); | 499 | .into_iter() |
500 | .map(|item| to_proto::completion_item(&line_index, line_endings, item)) | ||
501 | .collect(); | ||
465 | 502 | ||
466 | Ok(Some(items.into())) | 503 | Ok(Some(items.into())) |
467 | } | 504 | } |
@@ -471,52 +508,51 @@ pub fn handle_folding_range( | |||
471 | params: FoldingRangeParams, | 508 | params: FoldingRangeParams, |
472 | ) -> Result<Option<Vec<FoldingRange>>> { | 509 | ) -> Result<Option<Vec<FoldingRange>>> { |
473 | let _p = profile("handle_folding_range"); | 510 | let _p = profile("handle_folding_range"); |
474 | let file_id = params.text_document.try_conv_with(&world)?; | 511 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
475 | let folds = world.analysis().folding_ranges(file_id)?; | 512 | let folds = world.analysis().folding_ranges(file_id)?; |
476 | let text = world.analysis().file_text(file_id)?; | 513 | let text = world.analysis().file_text(file_id)?; |
477 | let line_index = world.analysis().file_line_index(file_id)?; | 514 | let line_index = world.analysis().file_line_index(file_id)?; |
478 | let ctx = FoldConvCtx { | 515 | let line_folding_only = world.config.client_caps.line_folding_only; |
479 | text: &text, | 516 | let res = folds |
480 | line_index: &line_index, | 517 | .into_iter() |
481 | line_folding_only: world.config.client_caps.line_folding_only, | 518 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) |
482 | }; | 519 | .collect(); |
483 | let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); | 520 | Ok(Some(res)) |
484 | Ok(res) | ||
485 | } | 521 | } |
486 | 522 | ||
487 | pub fn handle_signature_help( | 523 | pub fn handle_signature_help( |
488 | world: WorldSnapshot, | 524 | world: WorldSnapshot, |
489 | params: req::SignatureHelpParams, | 525 | params: lsp_types::SignatureHelpParams, |
490 | ) -> Result<Option<req::SignatureHelp>> { | 526 | ) -> Result<Option<lsp_types::SignatureHelp>> { |
491 | let _p = profile("handle_signature_help"); | 527 | let _p = profile("handle_signature_help"); |
492 | let position = params.text_document_position_params.try_conv_with(&world)?; | 528 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
493 | if let Some(call_info) = world.analysis().call_info(position)? { | 529 | let call_info = match world.analysis().call_info(position)? { |
494 | let concise = !world.config.call_info_full; | 530 | None => return Ok(None), |
495 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); | 531 | Some(it) => it, |
496 | if concise && call_info.signature.has_self_param { | 532 | }; |
497 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | 533 | let concise = !world.config.call_info_full; |
498 | } | 534 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); |
499 | let sig_info = call_info.signature.conv_with(concise); | 535 | if concise && call_info.signature.has_self_param { |
500 | 536 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | |
501 | Ok(Some(req::SignatureHelp { | ||
502 | signatures: vec![sig_info], | ||
503 | active_signature: Some(0), | ||
504 | active_parameter, | ||
505 | })) | ||
506 | } else { | ||
507 | Ok(None) | ||
508 | } | 537 | } |
538 | let sig_info = to_proto::signature_information(call_info.signature, concise); | ||
539 | |||
540 | Ok(Some(lsp_types::SignatureHelp { | ||
541 | signatures: vec![sig_info], | ||
542 | active_signature: Some(0), | ||
543 | active_parameter, | ||
544 | })) | ||
509 | } | 545 | } |
510 | 546 | ||
511 | pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> { | 547 | pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { |
512 | let _p = profile("handle_hover"); | 548 | let _p = profile("handle_hover"); |
513 | let position = params.text_document_position_params.try_conv_with(&world)?; | 549 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
514 | let info = match world.analysis().hover(position)? { | 550 | let info = match world.analysis().hover(position)? { |
515 | None => return Ok(None), | 551 | None => return Ok(None), |
516 | Some(info) => info, | 552 | Some(info) => info, |
517 | }; | 553 | }; |
518 | let line_index = world.analysis.file_line_index(position.file_id)?; | 554 | let line_index = world.analysis.file_line_index(position.file_id)?; |
519 | let range = info.range.conv_with(&line_index); | 555 | let range = to_proto::range(&line_index, info.range); |
520 | let res = Hover { | 556 | let res = Hover { |
521 | contents: HoverContents::Markup(MarkupContent { | 557 | contents: HoverContents::Markup(MarkupContent { |
522 | kind: MarkupKind::Markdown, | 558 | kind: MarkupKind::Markdown, |
@@ -529,10 +565,10 @@ pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Op | |||
529 | 565 | ||
530 | pub fn handle_prepare_rename( | 566 | pub fn handle_prepare_rename( |
531 | world: WorldSnapshot, | 567 | world: WorldSnapshot, |
532 | params: req::TextDocumentPositionParams, | 568 | params: lsp_types::TextDocumentPositionParams, |
533 | ) -> Result<Option<PrepareRenameResponse>> { | 569 | ) -> Result<Option<PrepareRenameResponse>> { |
534 | let _p = profile("handle_prepare_rename"); | 570 | let _p = profile("handle_prepare_rename"); |
535 | let position = params.try_conv_with(&world)?; | 571 | let position = from_proto::file_position(&world, params)?; |
536 | 572 | ||
537 | let optional_change = world.analysis().rename(position, "dummy")?; | 573 | let optional_change = world.analysis().rename(position, "dummy")?; |
538 | let range = match optional_change { | 574 | let range = match optional_change { |
@@ -540,15 +576,14 @@ pub fn handle_prepare_rename( | |||
540 | Some(it) => it.range, | 576 | Some(it) => it.range, |
541 | }; | 577 | }; |
542 | 578 | ||
543 | let file_id = params.text_document.try_conv_with(&world)?; | 579 | let line_index = world.analysis().file_line_index(position.file_id)?; |
544 | let line_index = world.analysis().file_line_index(file_id)?; | 580 | let range = to_proto::range(&line_index, range); |
545 | let range = range.conv_with(&line_index); | ||
546 | Ok(Some(PrepareRenameResponse::Range(range))) | 581 | Ok(Some(PrepareRenameResponse::Range(range))) |
547 | } | 582 | } |
548 | 583 | ||
549 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { | 584 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { |
550 | let _p = profile("handle_rename"); | 585 | let _p = profile("handle_rename"); |
551 | let position = params.text_document_position.try_conv_with(&world)?; | 586 | let position = from_proto::file_position(&world, params.text_document_position)?; |
552 | 587 | ||
553 | if params.new_name.is_empty() { | 588 | if params.new_name.is_empty() { |
554 | return Err(LspError::new( | 589 | return Err(LspError::new( |
@@ -559,22 +594,20 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio | |||
559 | } | 594 | } |
560 | 595 | ||
561 | let optional_change = world.analysis().rename(position, &*params.new_name)?; | 596 | let optional_change = world.analysis().rename(position, &*params.new_name)?; |
562 | let change = match optional_change { | 597 | let source_change = match optional_change { |
563 | None => return Ok(None), | 598 | None => return Ok(None), |
564 | Some(it) => it.info, | 599 | Some(it) => it.info, |
565 | }; | 600 | }; |
566 | 601 | let workspace_edit = to_proto::workspace_edit(&world, source_change)?; | |
567 | let source_change_req = change.try_conv_with(&world)?; | 602 | Ok(Some(workspace_edit)) |
568 | |||
569 | Ok(Some(source_change_req.workspace_edit)) | ||
570 | } | 603 | } |
571 | 604 | ||
572 | pub fn handle_references( | 605 | pub fn handle_references( |
573 | world: WorldSnapshot, | 606 | world: WorldSnapshot, |
574 | params: req::ReferenceParams, | 607 | params: lsp_types::ReferenceParams, |
575 | ) -> Result<Option<Vec<Location>>> { | 608 | ) -> Result<Option<Vec<Location>>> { |
576 | let _p = profile("handle_references"); | 609 | let _p = profile("handle_references"); |
577 | let position = params.text_document_position.try_conv_with(&world)?; | 610 | let position = from_proto::file_position(&world, params.text_document_position)?; |
578 | 611 | ||
579 | let refs = match world.analysis().find_all_refs(position, None)? { | 612 | let refs = match world.analysis().find_all_refs(position, None)? { |
580 | None => return Ok(None), | 613 | None => return Ok(None), |
@@ -583,33 +616,13 @@ pub fn handle_references( | |||
583 | 616 | ||
584 | let locations = if params.context.include_declaration { | 617 | let locations = if params.context.include_declaration { |
585 | refs.into_iter() | 618 | refs.into_iter() |
586 | .filter_map(|reference| { | 619 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) |
587 | let line_index = | ||
588 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; | ||
589 | to_location( | ||
590 | reference.file_range.file_id, | ||
591 | reference.file_range.range, | ||
592 | &world, | ||
593 | &line_index, | ||
594 | ) | ||
595 | .ok() | ||
596 | }) | ||
597 | .collect() | 620 | .collect() |
598 | } else { | 621 | } else { |
599 | // Only iterate over the references if include_declaration was false | 622 | // Only iterate over the references if include_declaration was false |
600 | refs.references() | 623 | refs.references() |
601 | .iter() | 624 | .iter() |
602 | .filter_map(|reference| { | 625 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) |
603 | let line_index = | ||
604 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; | ||
605 | to_location( | ||
606 | reference.file_range.file_id, | ||
607 | reference.file_range.range, | ||
608 | &world, | ||
609 | &line_index, | ||
610 | ) | ||
611 | .ok() | ||
612 | }) | ||
613 | .collect() | 626 | .collect() |
614 | }; | 627 | }; |
615 | 628 | ||
@@ -619,14 +632,14 @@ pub fn handle_references( | |||
619 | pub fn handle_formatting( | 632 | pub fn handle_formatting( |
620 | world: WorldSnapshot, | 633 | world: WorldSnapshot, |
621 | params: DocumentFormattingParams, | 634 | params: DocumentFormattingParams, |
622 | ) -> Result<Option<Vec<TextEdit>>> { | 635 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
623 | let _p = profile("handle_formatting"); | 636 | let _p = profile("handle_formatting"); |
624 | let file_id = params.text_document.try_conv_with(&world)?; | 637 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
625 | let file = world.analysis().file_text(file_id)?; | 638 | let file = world.analysis().file_text(file_id)?; |
626 | let crate_ids = world.analysis().crate_for(file_id)?; | 639 | let crate_ids = world.analysis().crate_for(file_id)?; |
627 | 640 | ||
628 | let file_line_index = world.analysis().file_line_index(file_id)?; | 641 | let file_line_index = world.analysis().file_line_index(file_id)?; |
629 | let end_position = TextSize::of(file.as_str()).conv_with(&file_line_index); | 642 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); |
630 | 643 | ||
631 | let mut rustfmt = match &world.config.rustfmt { | 644 | let mut rustfmt = match &world.config.rustfmt { |
632 | RustfmtConfig::Rustfmt { extra_args } => { | 645 | RustfmtConfig::Rustfmt { extra_args } => { |
@@ -686,42 +699,30 @@ pub fn handle_formatting( | |||
686 | } | 699 | } |
687 | } | 700 | } |
688 | 701 | ||
689 | Ok(Some(vec![TextEdit { | 702 | Ok(Some(vec![lsp_types::TextEdit { |
690 | range: Range::new(Position::new(0, 0), end_position), | 703 | range: Range::new(Position::new(0, 0), end_position), |
691 | new_text: captured_stdout, | 704 | new_text: captured_stdout, |
692 | }])) | 705 | }])) |
693 | } | 706 | } |
694 | 707 | ||
695 | fn create_single_code_action(assist: Assist, world: &WorldSnapshot) -> Result<CodeAction> { | ||
696 | let arg = to_value(assist.source_change.try_conv_with(world)?)?; | ||
697 | let title = assist.label; | ||
698 | let command = Command { | ||
699 | title: title.clone(), | ||
700 | command: "rust-analyzer.applySourceChange".to_string(), | ||
701 | arguments: Some(vec![arg]), | ||
702 | }; | ||
703 | |||
704 | Ok(CodeAction { | ||
705 | title, | ||
706 | kind: Some(String::new()), | ||
707 | diagnostics: None, | ||
708 | edit: None, | ||
709 | command: Some(command), | ||
710 | is_preferred: None, | ||
711 | }) | ||
712 | } | ||
713 | |||
714 | pub fn handle_code_action( | 708 | pub fn handle_code_action( |
715 | world: WorldSnapshot, | 709 | world: WorldSnapshot, |
716 | params: req::CodeActionParams, | 710 | params: lsp_types::CodeActionParams, |
717 | ) -> Result<Option<CodeActionResponse>> { | 711 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
718 | let _p = profile("handle_code_action"); | 712 | let _p = profile("handle_code_action"); |
719 | let file_id = params.text_document.try_conv_with(&world)?; | 713 | // We intentionally don't support command-based actions, as those either |
714 | // requires custom client-code anyway, or requires server-initiated edits. | ||
715 | // Server initiated edits break causality, so we avoid those as well. | ||
716 | if !world.config.client_caps.code_action_literals { | ||
717 | return Ok(None); | ||
718 | } | ||
719 | |||
720 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | ||
720 | let line_index = world.analysis().file_line_index(file_id)?; | 721 | let line_index = world.analysis().file_line_index(file_id)?; |
721 | let range = params.range.conv_with(&line_index); | 722 | let range = from_proto::text_range(&line_index, params.range); |
722 | 723 | ||
723 | let diagnostics = world.analysis().diagnostics(file_id)?; | 724 | let diagnostics = world.analysis().diagnostics(file_id)?; |
724 | let mut res = CodeActionResponse::default(); | 725 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
725 | 726 | ||
726 | let fixes_from_diagnostics = diagnostics | 727 | let fixes_from_diagnostics = diagnostics |
727 | .into_iter() | 728 | .into_iter() |
@@ -731,26 +732,13 @@ pub fn handle_code_action( | |||
731 | 732 | ||
732 | for source_edit in fixes_from_diagnostics { | 733 | for source_edit in fixes_from_diagnostics { |
733 | let title = source_edit.label.clone(); | 734 | let title = source_edit.label.clone(); |
734 | let edit = source_edit.try_conv_with(&world)?; | 735 | let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; |
735 | 736 | let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; | |
736 | let command = Command { | 737 | res.push(action); |
737 | title, | ||
738 | command: "rust-analyzer.applySourceChange".to_string(), | ||
739 | arguments: Some(vec![to_value(edit).unwrap()]), | ||
740 | }; | ||
741 | let action = CodeAction { | ||
742 | title: command.title.clone(), | ||
743 | kind: None, | ||
744 | diagnostics: None, | ||
745 | edit: None, | ||
746 | command: Some(command), | ||
747 | is_preferred: None, | ||
748 | }; | ||
749 | res.push(action.into()); | ||
750 | } | 738 | } |
751 | 739 | ||
752 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { | 740 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { |
753 | let fix_range = fix.range.conv_with(&line_index); | 741 | let fix_range = from_proto::text_range(&line_index, fix.range); |
754 | if fix_range.intersect(range).is_none() { | 742 | if fix_range.intersect(range).is_none() { |
755 | continue; | 743 | continue; |
756 | } | 744 | } |
@@ -758,34 +746,41 @@ pub fn handle_code_action( | |||
758 | } | 746 | } |
759 | 747 | ||
760 | let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); | 748 | let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); |
761 | for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { | 749 | for assist in |
750 | world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter() | ||
751 | { | ||
762 | match &assist.group_label { | 752 | match &assist.group_label { |
763 | Some(label) => grouped_assists | 753 | Some(label) => grouped_assists |
764 | .entry(label.to_owned()) | 754 | .entry(label.to_owned()) |
765 | .or_insert_with(|| { | 755 | .or_insert_with(|| { |
766 | let idx = res.len(); | 756 | let idx = res.len(); |
767 | let dummy = Command::new(String::new(), String::new(), None); | 757 | let dummy = lsp_ext::CodeAction { |
768 | res.push(dummy.into()); | 758 | title: String::new(), |
759 | kind: None, | ||
760 | command: None, | ||
761 | edit: None, | ||
762 | }; | ||
763 | res.push(dummy); | ||
769 | (idx, Vec::new()) | 764 | (idx, Vec::new()) |
770 | }) | 765 | }) |
771 | .1 | 766 | .1 |
772 | .push(assist), | 767 | .push(assist), |
773 | None => { | 768 | None => { |
774 | res.push(create_single_code_action(assist, &world)?.into()); | 769 | res.push(to_proto::code_action(&world, assist)?.into()); |
775 | } | 770 | } |
776 | } | 771 | } |
777 | } | 772 | } |
778 | 773 | ||
779 | for (group_label, (idx, assists)) in grouped_assists { | 774 | for (group_label, (idx, assists)) in grouped_assists { |
780 | if assists.len() == 1 { | 775 | if assists.len() == 1 { |
781 | res[idx] = | 776 | res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into(); |
782 | create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into(); | ||
783 | } else { | 777 | } else { |
784 | let title = group_label; | 778 | let title = group_label; |
785 | 779 | ||
786 | let mut arguments = Vec::with_capacity(assists.len()); | 780 | let mut arguments = Vec::with_capacity(assists.len()); |
787 | for assist in assists { | 781 | for assist in assists { |
788 | arguments.push(to_value(assist.source_change.try_conv_with(&world)?)?); | 782 | let source_change = to_proto::source_change(&world, assist.source_change)?; |
783 | arguments.push(to_value(source_change)?); | ||
789 | } | 784 | } |
790 | 785 | ||
791 | let command = Some(Command { | 786 | let command = Some(Command { |
@@ -793,121 +788,127 @@ pub fn handle_code_action( | |||
793 | command: "rust-analyzer.selectAndApplySourceChange".to_string(), | 788 | command: "rust-analyzer.selectAndApplySourceChange".to_string(), |
794 | arguments: Some(vec![serde_json::Value::Array(arguments)]), | 789 | arguments: Some(vec![serde_json::Value::Array(arguments)]), |
795 | }); | 790 | }); |
796 | res[idx] = CodeAction { | 791 | res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command }; |
797 | title, | ||
798 | kind: None, | ||
799 | diagnostics: None, | ||
800 | edit: None, | ||
801 | command, | ||
802 | is_preferred: None, | ||
803 | } | ||
804 | .into(); | ||
805 | } | 792 | } |
806 | } | 793 | } |
807 | 794 | ||
808 | // If the client only supports commands then filter the list | ||
809 | // and remove and actions that depend on edits. | ||
810 | if !world.config.client_caps.code_action_literals { | ||
811 | // FIXME: use drain_filter once it hits stable. | ||
812 | res = res | ||
813 | .into_iter() | ||
814 | .filter_map(|it| match it { | ||
815 | cmd @ lsp_types::CodeActionOrCommand::Command(_) => Some(cmd), | ||
816 | lsp_types::CodeActionOrCommand::CodeAction(action) => match action.command { | ||
817 | Some(cmd) if action.edit.is_none() => { | ||
818 | Some(lsp_types::CodeActionOrCommand::Command(cmd)) | ||
819 | } | ||
820 | _ => None, | ||
821 | }, | ||
822 | }) | ||
823 | .collect(); | ||
824 | } | ||
825 | Ok(Some(res)) | 795 | Ok(Some(res)) |
826 | } | 796 | } |
827 | 797 | ||
828 | pub fn handle_code_lens( | 798 | pub fn handle_code_lens( |
829 | world: WorldSnapshot, | 799 | world: WorldSnapshot, |
830 | params: req::CodeLensParams, | 800 | params: lsp_types::CodeLensParams, |
831 | ) -> Result<Option<Vec<CodeLens>>> { | 801 | ) -> Result<Option<Vec<CodeLens>>> { |
832 | let _p = profile("handle_code_lens"); | 802 | let _p = profile("handle_code_lens"); |
833 | let file_id = params.text_document.try_conv_with(&world)?; | ||
834 | let line_index = world.analysis().file_line_index(file_id)?; | ||
835 | |||
836 | let mut lenses: Vec<CodeLens> = Default::default(); | 803 | let mut lenses: Vec<CodeLens> = Default::default(); |
837 | 804 | ||
838 | // Gather runnables | 805 | if world.config.lens.none() { |
839 | for runnable in world.analysis().runnables(file_id)? { | 806 | // early return before any db query! |
840 | let title = match &runnable.kind { | 807 | return Ok(Some(lenses)); |
841 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", | ||
842 | RunnableKind::Bench { .. } => "Run Bench", | ||
843 | RunnableKind::Bin => "Run", | ||
844 | } | ||
845 | .to_string(); | ||
846 | let mut r = to_lsp_runnable(&world, file_id, runnable)?; | ||
847 | let lens = CodeLens { | ||
848 | range: r.range, | ||
849 | command: Some(Command { | ||
850 | title, | ||
851 | command: "rust-analyzer.runSingle".into(), | ||
852 | arguments: Some(vec![to_value(&r).unwrap()]), | ||
853 | }), | ||
854 | data: None, | ||
855 | }; | ||
856 | lenses.push(lens); | ||
857 | |||
858 | if r.args[0] == "run" { | ||
859 | r.args[0] = "build".into(); | ||
860 | } else { | ||
861 | r.args.push("--no-run".into()); | ||
862 | } | ||
863 | let debug_lens = CodeLens { | ||
864 | range: r.range, | ||
865 | command: Some(Command { | ||
866 | title: "Debug".into(), | ||
867 | command: "rust-analyzer.debugSingle".into(), | ||
868 | arguments: Some(vec![to_value(r).unwrap()]), | ||
869 | }), | ||
870 | data: None, | ||
871 | }; | ||
872 | lenses.push(debug_lens); | ||
873 | } | 808 | } |
874 | 809 | ||
875 | // Handle impls | 810 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
876 | lenses.extend( | 811 | let line_index = world.analysis().file_line_index(file_id)?; |
877 | world | 812 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; |
878 | .analysis() | 813 | |
879 | .file_structure(file_id)? | 814 | if world.config.lens.runnable() { |
880 | .into_iter() | 815 | // Gather runnables |
881 | .filter(|it| match it.kind { | 816 | for runnable in world.analysis().runnables(file_id)? { |
882 | SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, | 817 | let (run_title, debugee) = match &runnable.kind { |
883 | _ => false, | 818 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { |
884 | }) | 819 | ("▶\u{fe0e} Run Test", true) |
885 | .map(|it| { | 820 | } |
886 | let range = it.node_range.conv_with(&line_index); | 821 | RunnableKind::DocTest { .. } => { |
887 | let pos = range.start; | 822 | // cargo does not support -no-run for doctests |
888 | let lens_params = req::GotoImplementationParams { | 823 | ("▶\u{fe0e} Run Doctest", false) |
889 | text_document_position_params: req::TextDocumentPositionParams::new( | 824 | } |
890 | params.text_document.clone(), | 825 | RunnableKind::Bench { .. } => { |
891 | pos, | 826 | // Nothing wrong with bench debugging |
892 | ), | 827 | ("Run Bench", true) |
893 | work_done_progress_params: Default::default(), | 828 | } |
894 | partial_result_params: Default::default(), | 829 | RunnableKind::Bin => { |
830 | // Do not suggest binary run on other target than binary | ||
831 | match &cargo_spec { | ||
832 | Some(spec) => match spec.target_kind { | ||
833 | TargetKind::Bin => ("Run", true), | ||
834 | _ => continue, | ||
835 | }, | ||
836 | None => continue, | ||
837 | } | ||
838 | } | ||
839 | }; | ||
840 | |||
841 | let mut r = to_lsp_runnable(&world, file_id, runnable)?; | ||
842 | if world.config.lens.run { | ||
843 | let lens = CodeLens { | ||
844 | range: r.range, | ||
845 | command: Some(Command { | ||
846 | title: run_title.to_string(), | ||
847 | command: "rust-analyzer.runSingle".into(), | ||
848 | arguments: Some(vec![to_value(&r).unwrap()]), | ||
849 | }), | ||
850 | data: None, | ||
895 | }; | 851 | }; |
896 | CodeLens { | 852 | lenses.push(lens); |
897 | range, | 853 | } |
898 | command: None, | 854 | |
899 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), | 855 | if debugee && world.config.lens.debug { |
856 | if r.args[0] == "run" { | ||
857 | r.args[0] = "build".into(); | ||
858 | } else { | ||
859 | r.args.push("--no-run".into()); | ||
900 | } | 860 | } |
901 | }), | 861 | let debug_lens = CodeLens { |
902 | ); | 862 | range: r.range, |
863 | command: Some(Command { | ||
864 | title: "Debug".into(), | ||
865 | command: "rust-analyzer.debugSingle".into(), | ||
866 | arguments: Some(vec![to_value(r).unwrap()]), | ||
867 | }), | ||
868 | data: None, | ||
869 | }; | ||
870 | lenses.push(debug_lens); | ||
871 | } | ||
872 | } | ||
873 | } | ||
903 | 874 | ||
875 | if world.config.lens.impementations { | ||
876 | // Handle impls | ||
877 | lenses.extend( | ||
878 | world | ||
879 | .analysis() | ||
880 | .file_structure(file_id)? | ||
881 | .into_iter() | ||
882 | .filter(|it| match it.kind { | ||
883 | SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, | ||
884 | _ => false, | ||
885 | }) | ||
886 | .map(|it| { | ||
887 | let range = to_proto::range(&line_index, it.node_range); | ||
888 | let pos = range.start; | ||
889 | let lens_params = lsp_types::request::GotoImplementationParams { | ||
890 | text_document_position_params: lsp_types::TextDocumentPositionParams::new( | ||
891 | params.text_document.clone(), | ||
892 | pos, | ||
893 | ), | ||
894 | work_done_progress_params: Default::default(), | ||
895 | partial_result_params: Default::default(), | ||
896 | }; | ||
897 | CodeLens { | ||
898 | range, | ||
899 | command: None, | ||
900 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), | ||
901 | } | ||
902 | }), | ||
903 | ); | ||
904 | } | ||
904 | Ok(Some(lenses)) | 905 | Ok(Some(lenses)) |
905 | } | 906 | } |
906 | 907 | ||
907 | #[derive(Debug, Serialize, Deserialize)] | 908 | #[derive(Debug, Serialize, Deserialize)] |
908 | #[serde(rename_all = "camelCase")] | 909 | #[serde(rename_all = "camelCase")] |
909 | enum CodeLensResolveData { | 910 | enum CodeLensResolveData { |
910 | Impls(req::GotoImplementationParams), | 911 | Impls(lsp_types::request::GotoImplementationParams), |
911 | } | 912 | } |
912 | 913 | ||
913 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { | 914 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { |
@@ -918,9 +919,9 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
918 | Some(CodeLensResolveData::Impls(lens_params)) => { | 919 | Some(CodeLensResolveData::Impls(lens_params)) => { |
919 | let locations: Vec<Location> = | 920 | let locations: Vec<Location> = |
920 | match handle_goto_implementation(world, lens_params.clone())? { | 921 | match handle_goto_implementation(world, lens_params.clone())? { |
921 | Some(req::GotoDefinitionResponse::Scalar(loc)) => vec![loc], | 922 | Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], |
922 | Some(req::GotoDefinitionResponse::Array(locs)) => locs, | 923 | Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, |
923 | Some(req::GotoDefinitionResponse::Link(links)) => links | 924 | Some(lsp_types::GotoDefinitionResponse::Link(links)) => links |
924 | .into_iter() | 925 | .into_iter() |
925 | .map(|link| Location::new(link.target_uri, link.target_selection_range)) | 926 | .map(|link| Location::new(link.target_uri, link.target_selection_range)) |
926 | .collect(), | 927 | .collect(), |
@@ -957,37 +958,39 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
957 | 958 | ||
958 | pub fn handle_document_highlight( | 959 | pub fn handle_document_highlight( |
959 | world: WorldSnapshot, | 960 | world: WorldSnapshot, |
960 | params: req::DocumentHighlightParams, | 961 | params: lsp_types::DocumentHighlightParams, |
961 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 962 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
962 | let _p = profile("handle_document_highlight"); | 963 | let _p = profile("handle_document_highlight"); |
963 | let file_id = params.text_document_position_params.text_document.try_conv_with(&world)?; | 964 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
964 | let line_index = world.analysis().file_line_index(file_id)?; | 965 | let line_index = world.analysis().file_line_index(position.file_id)?; |
965 | 966 | ||
966 | let refs = match world.analysis().find_all_refs( | 967 | let refs = match world |
967 | params.text_document_position_params.try_conv_with(&world)?, | 968 | .analysis() |
968 | Some(SearchScope::single_file(file_id)), | 969 | .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? |
969 | )? { | 970 | { |
970 | None => return Ok(None), | 971 | None => return Ok(None), |
971 | Some(refs) => refs, | 972 | Some(refs) => refs, |
972 | }; | 973 | }; |
973 | 974 | ||
974 | Ok(Some( | 975 | let res = refs |
975 | refs.into_iter() | 976 | .into_iter() |
976 | .filter(|reference| reference.file_range.file_id == file_id) | 977 | .filter(|reference| reference.file_range.file_id == position.file_id) |
977 | .map(|reference| DocumentHighlight { | 978 | .map(|reference| DocumentHighlight { |
978 | range: reference.file_range.range.conv_with(&line_index), | 979 | range: to_proto::range(&line_index, reference.file_range.range), |
979 | kind: reference.access.map(|it| it.conv()), | 980 | kind: reference.access.map(to_proto::document_highlight_kind), |
980 | }) | 981 | }) |
981 | .collect(), | 982 | .collect(); |
982 | )) | 983 | Ok(Some(res)) |
983 | } | 984 | } |
984 | 985 | ||
985 | pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { | 986 | pub fn handle_ssr( |
987 | world: WorldSnapshot, | ||
988 | params: lsp_ext::SsrParams, | ||
989 | ) -> Result<lsp_ext::SourceChange> { | ||
986 | let _p = profile("handle_ssr"); | 990 | let _p = profile("handle_ssr"); |
987 | world | 991 | let source_change = |
988 | .analysis() | 992 | world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; |
989 | .structural_search_replace(¶ms.query, params.parse_only)?? | 993 | to_proto::source_change(&world, source_change) |
990 | .try_conv_with(&world) | ||
991 | } | 994 | } |
992 | 995 | ||
993 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 996 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { |
@@ -998,8 +1001,8 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia | |||
998 | .diagnostics(file_id)? | 1001 | .diagnostics(file_id)? |
999 | .into_iter() | 1002 | .into_iter() |
1000 | .map(|d| Diagnostic { | 1003 | .map(|d| Diagnostic { |
1001 | range: d.range.conv_with(&line_index), | 1004 | range: to_proto::range(&line_index, d.range), |
1002 | severity: Some(d.severity.conv()), | 1005 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1003 | code: None, | 1006 | code: None, |
1004 | source: Some("rust-analyzer".to_string()), | 1007 | source: Some("rust-analyzer".to_string()), |
1005 | message: d.message, | 1008 | message: d.message, |
@@ -1014,18 +1017,22 @@ fn to_lsp_runnable( | |||
1014 | world: &WorldSnapshot, | 1017 | world: &WorldSnapshot, |
1015 | file_id: FileId, | 1018 | file_id: FileId, |
1016 | runnable: Runnable, | 1019 | runnable: Runnable, |
1017 | ) -> Result<req::Runnable> { | 1020 | ) -> Result<lsp_ext::Runnable> { |
1018 | let spec = CargoTargetSpec::for_file(world, file_id)?; | 1021 | let spec = CargoTargetSpec::for_file(world, file_id)?; |
1022 | let target = spec.as_ref().map(|s| s.target.clone()); | ||
1019 | let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; | 1023 | let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; |
1020 | let line_index = world.analysis().file_line_index(file_id)?; | 1024 | let line_index = world.analysis().file_line_index(file_id)?; |
1021 | let label = match &runnable.kind { | 1025 | let label = match &runnable.kind { |
1022 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | 1026 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), |
1023 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | 1027 | RunnableKind::TestMod { path } => format!("test-mod {}", path), |
1024 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | 1028 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), |
1025 | RunnableKind::Bin => "run binary".to_string(), | 1029 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), |
1030 | RunnableKind::Bin => { | ||
1031 | target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) | ||
1032 | } | ||
1026 | }; | 1033 | }; |
1027 | Ok(req::Runnable { | 1034 | Ok(lsp_ext::Runnable { |
1028 | range: runnable.range.conv_with(&line_index), | 1035 | range: to_proto::range(&line_index, runnable.range), |
1029 | label, | 1036 | label, |
1030 | bin: "cargo".to_string(), | 1037 | bin: "cargo".to_string(), |
1031 | args, | 1038 | args, |
@@ -1044,13 +1051,13 @@ pub fn handle_inlay_hints( | |||
1044 | params: InlayHintsParams, | 1051 | params: InlayHintsParams, |
1045 | ) -> Result<Vec<InlayHint>> { | 1052 | ) -> Result<Vec<InlayHint>> { |
1046 | let _p = profile("handle_inlay_hints"); | 1053 | let _p = profile("handle_inlay_hints"); |
1047 | let file_id = params.text_document.try_conv_with(&world)?; | 1054 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
1048 | let analysis = world.analysis(); | 1055 | let analysis = world.analysis(); |
1049 | let line_index = analysis.file_line_index(file_id)?; | 1056 | let line_index = analysis.file_line_index(file_id)?; |
1050 | Ok(analysis | 1057 | Ok(analysis |
1051 | .inlay_hints(file_id, &world.config.inlay_hints)? | 1058 | .inlay_hints(file_id, &world.config.inlay_hints)? |
1052 | .into_iter() | 1059 | .into_iter() |
1053 | .map_conv_with(&line_index) | 1060 | .map(|it| to_proto::inlay_int(&line_index, it)) |
1054 | .collect()) | 1061 | .collect()) |
1055 | } | 1062 | } |
1056 | 1063 | ||
@@ -1059,21 +1066,19 @@ pub fn handle_call_hierarchy_prepare( | |||
1059 | params: CallHierarchyPrepareParams, | 1066 | params: CallHierarchyPrepareParams, |
1060 | ) -> Result<Option<Vec<CallHierarchyItem>>> { | 1067 | ) -> Result<Option<Vec<CallHierarchyItem>>> { |
1061 | let _p = profile("handle_call_hierarchy_prepare"); | 1068 | let _p = profile("handle_call_hierarchy_prepare"); |
1062 | let position = params.text_document_position_params.try_conv_with(&world)?; | 1069 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
1063 | let file_id = position.file_id; | ||
1064 | 1070 | ||
1065 | let nav_info = match world.analysis().call_hierarchy(position)? { | 1071 | let nav_info = match world.analysis().call_hierarchy(position)? { |
1066 | None => return Ok(None), | 1072 | None => return Ok(None), |
1067 | Some(it) => it, | 1073 | Some(it) => it, |
1068 | }; | 1074 | }; |
1069 | 1075 | ||
1070 | let line_index = world.analysis().file_line_index(file_id)?; | 1076 | let RangeInfo { range: _, info: navs } = nav_info; |
1071 | let RangeInfo { range, info: navs } = nav_info; | ||
1072 | let res = navs | 1077 | let res = navs |
1073 | .into_iter() | 1078 | .into_iter() |
1074 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) | 1079 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) |
1075 | .filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok()) | 1080 | .map(|it| to_proto::call_hierarchy_item(&world, it)) |
1076 | .collect(); | 1081 | .collect::<Result<Vec<_>>>()?; |
1077 | 1082 | ||
1078 | Ok(Some(res)) | 1083 | Ok(Some(res)) |
1079 | } | 1084 | } |
@@ -1086,7 +1091,7 @@ pub fn handle_call_hierarchy_incoming( | |||
1086 | let item = params.item; | 1091 | let item = params.item; |
1087 | 1092 | ||
1088 | let doc = TextDocumentIdentifier::new(item.uri); | 1093 | let doc = TextDocumentIdentifier::new(item.uri); |
1089 | let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; | 1094 | let frange = from_proto::file_range(&world, doc, item.range)?; |
1090 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1095 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1091 | 1096 | ||
1092 | let call_items = match world.analysis().incoming_calls(fpos)? { | 1097 | let call_items = match world.analysis().incoming_calls(fpos)? { |
@@ -1099,11 +1104,14 @@ pub fn handle_call_hierarchy_incoming( | |||
1099 | for call_item in call_items.into_iter() { | 1104 | for call_item in call_items.into_iter() { |
1100 | let file_id = call_item.target.file_id(); | 1105 | let file_id = call_item.target.file_id(); |
1101 | let line_index = world.analysis().file_line_index(file_id)?; | 1106 | let line_index = world.analysis().file_line_index(file_id)?; |
1102 | let range = call_item.target.range(); | 1107 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; |
1103 | let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; | ||
1104 | res.push(CallHierarchyIncomingCall { | 1108 | res.push(CallHierarchyIncomingCall { |
1105 | from: item, | 1109 | from: item, |
1106 | from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), | 1110 | from_ranges: call_item |
1111 | .ranges | ||
1112 | .into_iter() | ||
1113 | .map(|it| to_proto::range(&line_index, it)) | ||
1114 | .collect(), | ||
1107 | }); | 1115 | }); |
1108 | } | 1116 | } |
1109 | 1117 | ||
@@ -1118,7 +1126,7 @@ pub fn handle_call_hierarchy_outgoing( | |||
1118 | let item = params.item; | 1126 | let item = params.item; |
1119 | 1127 | ||
1120 | let doc = TextDocumentIdentifier::new(item.uri); | 1128 | let doc = TextDocumentIdentifier::new(item.uri); |
1121 | let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; | 1129 | let frange = from_proto::file_range(&world, doc, item.range)?; |
1122 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1130 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1123 | 1131 | ||
1124 | let call_items = match world.analysis().outgoing_calls(fpos)? { | 1132 | let call_items = match world.analysis().outgoing_calls(fpos)? { |
@@ -1131,11 +1139,14 @@ pub fn handle_call_hierarchy_outgoing( | |||
1131 | for call_item in call_items.into_iter() { | 1139 | for call_item in call_items.into_iter() { |
1132 | let file_id = call_item.target.file_id(); | 1140 | let file_id = call_item.target.file_id(); |
1133 | let line_index = world.analysis().file_line_index(file_id)?; | 1141 | let line_index = world.analysis().file_line_index(file_id)?; |
1134 | let range = call_item.target.range(); | 1142 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; |
1135 | let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; | ||
1136 | res.push(CallHierarchyOutgoingCall { | 1143 | res.push(CallHierarchyOutgoingCall { |
1137 | to: item, | 1144 | to: item, |
1138 | from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), | 1145 | from_ranges: call_item |
1146 | .ranges | ||
1147 | .into_iter() | ||
1148 | .map(|it| to_proto::range(&line_index, it)) | ||
1149 | .collect(), | ||
1139 | }); | 1150 | }); |
1140 | } | 1151 | } |
1141 | 1152 | ||
@@ -1148,26 +1159,13 @@ pub fn handle_semantic_tokens( | |||
1148 | ) -> Result<Option<SemanticTokensResult>> { | 1159 | ) -> Result<Option<SemanticTokensResult>> { |
1149 | let _p = profile("handle_semantic_tokens"); | 1160 | let _p = profile("handle_semantic_tokens"); |
1150 | 1161 | ||
1151 | let file_id = params.text_document.try_conv_with(&world)?; | 1162 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
1152 | let text = world.analysis().file_text(file_id)?; | 1163 | let text = world.analysis().file_text(file_id)?; |
1153 | let line_index = world.analysis().file_line_index(file_id)?; | 1164 | let line_index = world.analysis().file_line_index(file_id)?; |
1154 | 1165 | ||
1155 | let mut builder = SemanticTokensBuilder::default(); | 1166 | let highlights = world.analysis().highlight(file_id)?; |
1156 | 1167 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | |
1157 | for highlight_range in world.analysis().highlight(file_id)?.into_iter() { | 1168 | Ok(Some(semantic_tokens.into())) |
1158 | let (token_index, modifier_bitset) = highlight_range.highlight.conv(); | ||
1159 | for mut range in line_index.lines(highlight_range.range) { | ||
1160 | if text[range].ends_with('\n') { | ||
1161 | range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); | ||
1162 | } | ||
1163 | let range = range.conv_with(&line_index); | ||
1164 | builder.push(range, token_index, modifier_bitset); | ||
1165 | } | ||
1166 | } | ||
1167 | |||
1168 | let tokens = builder.build(); | ||
1169 | |||
1170 | Ok(Some(tokens.into())) | ||
1171 | } | 1169 | } |
1172 | 1170 | ||
1173 | pub fn handle_semantic_tokens_range( | 1171 | pub fn handle_semantic_tokens_range( |
@@ -1176,17 +1174,11 @@ pub fn handle_semantic_tokens_range( | |||
1176 | ) -> Result<Option<SemanticTokensRangeResult>> { | 1174 | ) -> Result<Option<SemanticTokensRangeResult>> { |
1177 | let _p = profile("handle_semantic_tokens_range"); | 1175 | let _p = profile("handle_semantic_tokens_range"); |
1178 | 1176 | ||
1179 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | 1177 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; |
1178 | let text = world.analysis().file_text(frange.file_id)?; | ||
1180 | let line_index = world.analysis().file_line_index(frange.file_id)?; | 1179 | let line_index = world.analysis().file_line_index(frange.file_id)?; |
1181 | 1180 | ||
1182 | let mut builder = SemanticTokensBuilder::default(); | 1181 | let highlights = world.analysis().highlight_range(frange)?; |
1183 | 1182 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | |
1184 | for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { | 1183 | Ok(Some(semantic_tokens.into())) |
1185 | let (token_type, token_modifiers) = highlight_range.highlight.conv(); | ||
1186 | builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); | ||
1187 | } | ||
1188 | |||
1189 | let tokens = builder.build(); | ||
1190 | |||
1191 | Ok(Some(tokens.into())) | ||
1192 | } | 1184 | } |