aboutsummaryrefslogtreecommitdiff
path: root/crates/rust-analyzer/src/main_loop/handlers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/rust-analyzer/src/main_loop/handlers.rs')
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs800
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;
11use lsp_types::{ 11use 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};
21use ra_ide::{ 20use 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};
24use ra_prof::profile; 24use ra_prof::profile;
25use ra_project_model::TargetKind;
25use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; 26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
26use rustc_hash::FxHashMap; 27use rustc_hash::FxHashMap;
27use serde::{Deserialize, Serialize}; 28use serde::{Deserialize, Serialize};
@@ -31,14 +32,10 @@ use stdx::format_to;
31use crate::{ 32use 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::{
46pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 43pub 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
58pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> { 55pub 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, &params.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
67pub fn handle_expand_macro( 67pub 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, &params.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
85pub fn handle_selection_range( 85pub 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, &params.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
128pub fn handle_find_matching_brace( 128pub 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, &params.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
153pub fn handle_join_lines( 150pub 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 = (&params.text_document, params.range).try_conv_with(&world)?; 155 let file_id = from_proto::file_id(&world, &params.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
162pub fn handle_on_enter( 173pub 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`.
175pub fn handle_on_type_formatting( 186pub 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
214pub fn handle_document_symbol( 225pub 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, &params.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,
263fn 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
282pub fn handle_workspace_symbol( 293pub 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
326pub fn handle_goto_definition( 337pub 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
340pub fn handle_goto_implementation( 355pub 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
354pub fn handle_goto_type_definition( 373pub 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
368pub fn handle_parent_module( 391pub 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
377pub fn handle_runnables( 405pub 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, &params.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
429pub fn handle_completion( 464pub 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, &params.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
487pub fn handle_signature_help( 523pub 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
511pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> { 547pub 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
530pub fn handle_prepare_rename( 566pub 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
549pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 584pub 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
572pub fn handle_references( 605pub 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(
619pub fn handle_formatting( 632pub 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, &params.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
695fn 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
714pub fn handle_code_action( 708pub 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, &params.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
828pub fn handle_code_lens( 798pub 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, &params.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")]
909enum CodeLensResolveData { 910enum CodeLensResolveData {
910 Impls(req::GotoImplementationParams), 911 Impls(lsp_types::request::GotoImplementationParams),
911} 912}
912 913
913pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { 914pub 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
958pub fn handle_document_highlight( 959pub 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
985pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { 986pub 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(&params.query, params.parse_only)??;
989 .structural_search_replace(&params.query, params.parse_only)?? 993 to_proto::source_change(&world, source_change)
990 .try_conv_with(&world)
991} 994}
992 995
993pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 996pub 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, &params.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, &params.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
1173pub fn handle_semantic_tokens_range( 1171pub 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 = (&params.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}