]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/handlers.rs
Merge #7172
[rust.git] / crates / rust-analyzer / src / handlers.rs
1 //! This module is responsible for implementing handlers for Language Server
2 //! Protocol. The majority of requests are fulfilled by calling into the
3 //! `ide` crate.
4
5 use std::{
6     io::Write as _,
7     process::{self, Stdio},
8     sync::Arc,
9 };
10
11 use ide::{
12     CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
13     LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
14     SourceChange, SymbolKind, TextEdit,
15 };
16 use itertools::Itertools;
17 use lsp_server::ErrorCode;
18 use lsp_types::{
19     CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
20     CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
21     CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
22     DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
23     HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
24     SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
25     SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
26     SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
27 };
28 use project_model::TargetKind;
29 use serde::{Deserialize, Serialize};
30 use serde_json::to_value;
31 use stdx::{format_to, split_once};
32 use syntax::{algo, ast, AstNode, TextRange, TextSize};
33
34 use crate::{
35     cargo_target_spec::CargoTargetSpec,
36     config::RustfmtConfig,
37     diff::diff,
38     from_json, from_proto,
39     global_state::{GlobalState, GlobalStateSnapshot},
40     line_endings::LineEndings,
41     lsp_ext::{self, InlayHint, InlayHintsParams},
42     lsp_utils::all_edits_are_disjoint,
43     to_proto, LspError, Result,
44 };
45
46 pub(crate) fn handle_analyzer_status(
47     snap: GlobalStateSnapshot,
48     params: lsp_ext::AnalyzerStatusParams,
49 ) -> Result<String> {
50     let _p = profile::span("handle_analyzer_status");
51
52     let mut buf = String::new();
53
54     let mut file_id = None;
55     if let Some(tdi) = params.text_document {
56         match from_proto::file_id(&snap, &tdi.uri) {
57             Ok(it) => file_id = Some(it),
58             Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri),
59         }
60     }
61
62     if snap.workspaces.is_empty() {
63         buf.push_str("no workspaces\n")
64     } else {
65         buf.push_str("workspaces:\n");
66         for w in snap.workspaces.iter() {
67             format_to!(buf, "{} packages loaded\n", w.n_packages());
68         }
69     }
70     buf.push_str("\nanalysis:\n");
71     buf.push_str(
72         &snap
73             .analysis
74             .status(file_id)
75             .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
76     );
77     format_to!(buf, "\n\nrequests:\n");
78     let requests = snap.latest_requests.read();
79     for (is_last, r) in requests.iter() {
80         let mark = if is_last { "*" } else { " " };
81         format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis());
82     }
83     Ok(buf)
84 }
85
86 pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
87     let _p = profile::span("handle_memory_usage");
88     let mem = state.analysis_host.per_query_memory_usage();
89
90     let mut out = String::new();
91     for (name, bytes) in mem {
92         format_to!(out, "{:>8} {}\n", bytes, name);
93     }
94     Ok(out)
95 }
96
97 pub(crate) fn handle_syntax_tree(
98     snap: GlobalStateSnapshot,
99     params: lsp_ext::SyntaxTreeParams,
100 ) -> Result<String> {
101     let _p = profile::span("handle_syntax_tree");
102     let id = from_proto::file_id(&snap, &params.text_document.uri)?;
103     let line_index = snap.analysis.file_line_index(id)?;
104     let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
105     let res = snap.analysis.syntax_tree(id, text_range)?;
106     Ok(res)
107 }
108
109 pub(crate) fn handle_view_hir(
110     snap: GlobalStateSnapshot,
111     params: lsp_types::TextDocumentPositionParams,
112 ) -> Result<String> {
113     let _p = profile::span("handle_view_hir");
114     let position = from_proto::file_position(&snap, params)?;
115     let res = snap.analysis.view_hir(position)?;
116     Ok(res)
117 }
118
119 pub(crate) fn handle_expand_macro(
120     snap: GlobalStateSnapshot,
121     params: lsp_ext::ExpandMacroParams,
122 ) -> Result<Option<lsp_ext::ExpandedMacro>> {
123     let _p = profile::span("handle_expand_macro");
124     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
125     let line_index = snap.analysis.file_line_index(file_id)?;
126     let offset = from_proto::offset(&line_index, params.position);
127
128     let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?;
129     Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
130 }
131
132 pub(crate) fn handle_selection_range(
133     snap: GlobalStateSnapshot,
134     params: lsp_types::SelectionRangeParams,
135 ) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
136     let _p = profile::span("handle_selection_range");
137     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
138     let line_index = snap.analysis.file_line_index(file_id)?;
139     let res: Result<Vec<lsp_types::SelectionRange>> = params
140         .positions
141         .into_iter()
142         .map(|position| {
143             let offset = from_proto::offset(&line_index, position);
144             let mut ranges = Vec::new();
145             {
146                 let mut range = TextRange::new(offset, offset);
147                 loop {
148                     ranges.push(range);
149                     let frange = FileRange { file_id, range };
150                     let next = snap.analysis.extend_selection(frange)?;
151                     if next == range {
152                         break;
153                     } else {
154                         range = next
155                     }
156                 }
157             }
158             let mut range = lsp_types::SelectionRange {
159                 range: to_proto::range(&line_index, *ranges.last().unwrap()),
160                 parent: None,
161             };
162             for &r in ranges.iter().rev().skip(1) {
163                 range = lsp_types::SelectionRange {
164                     range: to_proto::range(&line_index, r),
165                     parent: Some(Box::new(range)),
166                 }
167             }
168             Ok(range)
169         })
170         .collect();
171
172     Ok(Some(res?))
173 }
174
175 pub(crate) fn handle_matching_brace(
176     snap: GlobalStateSnapshot,
177     params: lsp_ext::MatchingBraceParams,
178 ) -> Result<Vec<Position>> {
179     let _p = profile::span("handle_matching_brace");
180     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
181     let line_index = snap.analysis.file_line_index(file_id)?;
182     let res = params
183         .positions
184         .into_iter()
185         .map(|position| {
186             let offset = from_proto::offset(&line_index, position);
187             let offset = match snap.analysis.matching_brace(FilePosition { file_id, offset }) {
188                 Ok(Some(matching_brace_offset)) => matching_brace_offset,
189                 Err(_) | Ok(None) => offset,
190             };
191             to_proto::position(&line_index, offset)
192         })
193         .collect();
194     Ok(res)
195 }
196
197 pub(crate) fn handle_join_lines(
198     snap: GlobalStateSnapshot,
199     params: lsp_ext::JoinLinesParams,
200 ) -> Result<Vec<lsp_types::TextEdit>> {
201     let _p = profile::span("handle_join_lines");
202     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
203     let line_index = snap.analysis.file_line_index(file_id)?;
204     let line_endings = snap.file_line_endings(file_id);
205     let mut res = TextEdit::default();
206     for range in params.ranges {
207         let range = from_proto::text_range(&line_index, range);
208         let edit = snap.analysis.join_lines(FileRange { file_id, range })?;
209         match res.union(edit) {
210             Ok(()) => (),
211             Err(_edit) => {
212                 // just ignore overlapping edits
213             }
214         }
215     }
216     let res = to_proto::text_edit_vec(&line_index, line_endings, res);
217     Ok(res)
218 }
219
220 pub(crate) fn handle_on_enter(
221     snap: GlobalStateSnapshot,
222     params: lsp_types::TextDocumentPositionParams,
223 ) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
224     let _p = profile::span("handle_on_enter");
225     let position = from_proto::file_position(&snap, params)?;
226     let edit = match snap.analysis.on_enter(position)? {
227         None => return Ok(None),
228         Some(it) => it,
229     };
230     let line_index = snap.analysis.file_line_index(position.file_id)?;
231     let line_endings = snap.file_line_endings(position.file_id);
232     let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
233     Ok(Some(edit))
234 }
235
236 // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
237 pub(crate) fn handle_on_type_formatting(
238     snap: GlobalStateSnapshot,
239     params: lsp_types::DocumentOnTypeFormattingParams,
240 ) -> Result<Option<Vec<lsp_types::TextEdit>>> {
241     let _p = profile::span("handle_on_type_formatting");
242     let mut position = from_proto::file_position(&snap, params.text_document_position)?;
243     let line_index = snap.analysis.file_line_index(position.file_id)?;
244     let line_endings = snap.file_line_endings(position.file_id);
245
246     // in `ide`, the `on_type` invariant is that
247     // `text.char_at(position) == typed_char`.
248     position.offset -= TextSize::of('.');
249     let char_typed = params.ch.chars().next().unwrap_or('\0');
250     assert!({
251         let text = snap.analysis.file_text(position.file_id)?;
252         text[usize::from(position.offset)..].starts_with(char_typed)
253     });
254
255     // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
256     // but it requires precise cursor positioning to work, and one can't
257     // position the cursor with on_type formatting. So, let's just toggle this
258     // feature off here, hoping that we'll enable it one day, ðŸ˜¿.
259     if char_typed == '>' {
260         return Ok(None);
261     }
262
263     let edit = snap.analysis.on_char_typed(position, char_typed)?;
264     let mut edit = match edit {
265         Some(it) => it,
266         None => return Ok(None),
267     };
268
269     // This should be a single-file edit
270     let edit = edit.source_file_edits.pop().unwrap();
271
272     let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit);
273     Ok(Some(change))
274 }
275
276 pub(crate) fn handle_document_symbol(
277     snap: GlobalStateSnapshot,
278     params: lsp_types::DocumentSymbolParams,
279 ) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
280     let _p = profile::span("handle_document_symbol");
281     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
282     let line_index = snap.analysis.file_line_index(file_id)?;
283
284     let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
285
286     for symbol in snap.analysis.file_structure(file_id)? {
287         let mut tags = Vec::new();
288         if symbol.deprecated {
289             tags.push(SymbolTag::Deprecated)
290         };
291
292         #[allow(deprecated)]
293         let doc_symbol = DocumentSymbol {
294             name: symbol.label,
295             detail: symbol.detail,
296             kind: to_proto::symbol_kind(symbol.kind),
297             tags: Some(tags),
298             deprecated: Some(symbol.deprecated),
299             range: to_proto::range(&line_index, symbol.node_range),
300             selection_range: to_proto::range(&line_index, symbol.navigation_range),
301             children: None,
302         };
303         parents.push((doc_symbol, symbol.parent));
304     }
305
306     // Builds hierarchy from a flat list, in reverse order (so that indices
307     // makes sense)
308     let document_symbols = {
309         let mut acc = Vec::new();
310         while let Some((mut node, parent_idx)) = parents.pop() {
311             if let Some(children) = &mut node.children {
312                 children.reverse();
313             }
314             let parent = match parent_idx {
315                 None => &mut acc,
316                 Some(i) => parents[i].0.children.get_or_insert_with(Vec::new),
317             };
318             parent.push(node);
319         }
320         acc.reverse();
321         acc
322     };
323
324     let res = if snap.config.hierarchical_symbols() {
325         document_symbols.into()
326     } else {
327         let url = to_proto::url(&snap, file_id);
328         let mut symbol_information = Vec::<SymbolInformation>::new();
329         for symbol in document_symbols {
330             flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
331         }
332         symbol_information.into()
333     };
334     return Ok(Some(res));
335
336     fn flatten_document_symbol(
337         symbol: &DocumentSymbol,
338         container_name: Option<String>,
339         url: &Url,
340         res: &mut Vec<SymbolInformation>,
341     ) {
342         let mut tags = Vec::new();
343
344         #[allow(deprecated)]
345         match symbol.deprecated {
346             Some(true) => tags.push(SymbolTag::Deprecated),
347             _ => {}
348         }
349
350         #[allow(deprecated)]
351         res.push(SymbolInformation {
352             name: symbol.name.clone(),
353             kind: symbol.kind,
354             tags: Some(tags),
355             deprecated: symbol.deprecated,
356             location: Location::new(url.clone(), symbol.range),
357             container_name,
358         });
359
360         for child in symbol.children.iter().flatten() {
361             flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
362         }
363     }
364 }
365
366 pub(crate) fn handle_workspace_symbol(
367     snap: GlobalStateSnapshot,
368     params: lsp_types::WorkspaceSymbolParams,
369 ) -> Result<Option<Vec<SymbolInformation>>> {
370     let _p = profile::span("handle_workspace_symbol");
371     let all_symbols = params.query.contains('#');
372     let libs = params.query.contains('*');
373     let query = {
374         let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect();
375         let mut q = Query::new(query);
376         if !all_symbols {
377             q.only_types();
378         }
379         if libs {
380             q.libs();
381         }
382         q.limit(128);
383         q
384     };
385     let mut res = exec_query(&snap, query)?;
386     if res.is_empty() && !all_symbols {
387         let mut query = Query::new(params.query);
388         query.limit(128);
389         res = exec_query(&snap, query)?;
390     }
391
392     return Ok(Some(res));
393
394     fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
395         let mut res = Vec::new();
396         for nav in snap.analysis.symbol_search(query)? {
397             let container_name = nav.container_name.as_ref().map(|v| v.to_string());
398
399             #[allow(deprecated)]
400             let info = SymbolInformation {
401                 name: nav.name.to_string(),
402                 kind: nav
403                     .kind
404                     .map(to_proto::symbol_kind)
405                     .unwrap_or(lsp_types::SymbolKind::Variable),
406                 tags: None,
407                 location: to_proto::location_from_nav(snap, nav)?,
408                 container_name,
409                 deprecated: None,
410             };
411             res.push(info);
412         }
413         Ok(res)
414     }
415 }
416
417 pub(crate) fn handle_will_rename_files(
418     snap: GlobalStateSnapshot,
419     params: lsp_types::RenameFilesParams,
420 ) -> Result<Option<lsp_types::WorkspaceEdit>> {
421     let _p = profile::span("handle_will_rename_files");
422
423     let source_changes: Vec<SourceChange> = params
424         .files
425         .into_iter()
426         .filter_map(|file_rename| {
427             let from = Url::parse(&file_rename.old_uri).ok()?;
428             let to = Url::parse(&file_rename.new_uri).ok()?;
429
430             let from_path = from.to_file_path().ok()?;
431             let to_path = to.to_file_path().ok()?;
432
433             // Limit to single-level moves for now.
434             match (from_path.parent(), to_path.parent()) {
435                 (Some(p1), Some(p2)) if p1 == p2 => {
436                     let new_name = to_path.file_stem()?;
437                     let new_name = new_name.to_str()?;
438                     Some((snap.url_to_file_id(&from).ok()?, new_name.to_string()))
439                 }
440                 _ => None,
441             }
442         })
443         .filter_map(|(file_id, new_name)| {
444             snap.analysis.will_rename_file(file_id, &new_name).ok()?
445         })
446         .collect();
447
448     // Drop file system edits since we're just renaming things on the same level
449     let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect();
450     let source_change = SourceChange::from_edits(edits, Vec::new());
451
452     let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
453     Ok(Some(workspace_edit))
454 }
455
456 pub(crate) fn handle_goto_definition(
457     snap: GlobalStateSnapshot,
458     params: lsp_types::GotoDefinitionParams,
459 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
460     let _p = profile::span("handle_goto_definition");
461     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
462     let nav_info = match snap.analysis.goto_definition(position)? {
463         None => return Ok(None),
464         Some(it) => it,
465     };
466     let src = FileRange { file_id: position.file_id, range: nav_info.range };
467     let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
468     Ok(Some(res))
469 }
470
471 pub(crate) fn handle_goto_implementation(
472     snap: GlobalStateSnapshot,
473     params: lsp_types::request::GotoImplementationParams,
474 ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
475     let _p = profile::span("handle_goto_implementation");
476     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
477     let nav_info = match snap.analysis.goto_implementation(position)? {
478         None => return Ok(None),
479         Some(it) => it,
480     };
481     let src = FileRange { file_id: position.file_id, range: nav_info.range };
482     let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
483     Ok(Some(res))
484 }
485
486 pub(crate) fn handle_goto_type_definition(
487     snap: GlobalStateSnapshot,
488     params: lsp_types::request::GotoTypeDefinitionParams,
489 ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
490     let _p = profile::span("handle_goto_type_definition");
491     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
492     let nav_info = match snap.analysis.goto_type_definition(position)? {
493         None => return Ok(None),
494         Some(it) => it,
495     };
496     let src = FileRange { file_id: position.file_id, range: nav_info.range };
497     let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
498     Ok(Some(res))
499 }
500
501 pub(crate) fn handle_parent_module(
502     snap: GlobalStateSnapshot,
503     params: lsp_types::TextDocumentPositionParams,
504 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
505     let _p = profile::span("handle_parent_module");
506     let position = from_proto::file_position(&snap, params)?;
507     let navs = snap.analysis.parent_module(position)?;
508     let res = to_proto::goto_definition_response(&snap, None, navs)?;
509     Ok(Some(res))
510 }
511
512 pub(crate) fn handle_runnables(
513     snap: GlobalStateSnapshot,
514     params: lsp_ext::RunnablesParams,
515 ) -> Result<Vec<lsp_ext::Runnable>> {
516     let _p = profile::span("handle_runnables");
517     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
518     let line_index = snap.analysis.file_line_index(file_id)?;
519     let offset = params.position.map(|it| from_proto::offset(&line_index, it));
520     let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
521
522     let expect_test = match offset {
523         Some(offset) => {
524             let source_file = snap.analysis.parse(file_id)?;
525             algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
526                 .and_then(|it| it.path()?.segment()?.name_ref())
527                 .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
528         }
529         None => false,
530     };
531
532     let mut res = Vec::new();
533     for runnable in snap.analysis.runnables(file_id)? {
534         if let Some(offset) = offset {
535             if !runnable.nav.full_range.contains_inclusive(offset) {
536                 continue;
537             }
538         }
539         if should_skip_target(&runnable, cargo_spec.as_ref()) {
540             continue;
541         }
542         let mut runnable = to_proto::runnable(&snap, file_id, runnable)?;
543         if expect_test {
544             runnable.label = format!("{} + expect", runnable.label);
545             runnable.args.expect_test = Some(true);
546         }
547         res.push(runnable);
548     }
549
550     // Add `cargo check` and `cargo test` for all targets of the whole package
551     let config = snap.config.runnables();
552     match cargo_spec {
553         Some(spec) => {
554             for &cmd in ["check", "test"].iter() {
555                 res.push(lsp_ext::Runnable {
556                     label: format!("cargo {} -p {} --all-targets", cmd, spec.package),
557                     location: None,
558                     kind: lsp_ext::RunnableKind::Cargo,
559                     args: lsp_ext::CargoRunnable {
560                         workspace_root: Some(spec.workspace_root.clone().into()),
561                         override_cargo: config.override_cargo.clone(),
562                         cargo_args: vec![
563                             cmd.to_string(),
564                             "--package".to_string(),
565                             spec.package.clone(),
566                             "--all-targets".to_string(),
567                         ],
568                         cargo_extra_args: config.cargo_extra_args.clone(),
569                         executable_args: Vec::new(),
570                         expect_test: None,
571                     },
572                 })
573             }
574         }
575         None => {
576             res.push(lsp_ext::Runnable {
577                 label: "cargo check --workspace".to_string(),
578                 location: None,
579                 kind: lsp_ext::RunnableKind::Cargo,
580                 args: lsp_ext::CargoRunnable {
581                     workspace_root: None,
582                     override_cargo: config.override_cargo,
583                     cargo_args: vec!["check".to_string(), "--workspace".to_string()],
584                     cargo_extra_args: config.cargo_extra_args,
585                     executable_args: Vec::new(),
586                     expect_test: None,
587                 },
588             });
589         }
590     }
591     Ok(res)
592 }
593
594 pub(crate) fn handle_completion(
595     snap: GlobalStateSnapshot,
596     params: lsp_types::CompletionParams,
597 ) -> Result<Option<lsp_types::CompletionResponse>> {
598     let _p = profile::span("handle_completion");
599     let text_document_position = params.text_document_position.clone();
600     let position = from_proto::file_position(&snap, params.text_document_position)?;
601     let completion_triggered_after_single_colon = {
602         let mut res = false;
603         if let Some(ctx) = params.context {
604             if ctx.trigger_character.unwrap_or_default() == ":" {
605                 let source_file = snap.analysis.parse(position.file_id)?;
606                 let syntax = source_file.syntax();
607                 let text = syntax.text();
608                 if let Some(next_char) = text.char_at(position.offset) {
609                     let diff = TextSize::of(next_char) + TextSize::of(':');
610                     let prev_char = position.offset - diff;
611                     if text.char_at(prev_char) != Some(':') {
612                         res = true;
613                     }
614                 }
615             }
616         }
617         res
618     };
619     if completion_triggered_after_single_colon {
620         return Ok(None);
621     }
622
623     let completion_config = &snap.config.completion();
624     let items = match snap.analysis.completions(completion_config, position)? {
625         None => return Ok(None),
626         Some(items) => items,
627     };
628     let line_index = snap.analysis.file_line_index(position.file_id)?;
629     let line_endings = snap.file_line_endings(position.file_id);
630
631     let items: Vec<CompletionItem> = items
632         .into_iter()
633         .flat_map(|item| {
634             let mut new_completion_items =
635                 to_proto::completion_item(&line_index, line_endings, item.clone());
636
637             if completion_config.resolve_additional_edits_lazily() {
638                 for new_item in &mut new_completion_items {
639                     let _ = fill_resolve_data(&mut new_item.data, &item, &text_document_position)
640                         .take();
641                 }
642             }
643
644             new_completion_items
645         })
646         .collect();
647
648     let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
649     Ok(Some(completion_list.into()))
650 }
651
652 pub(crate) fn handle_completion_resolve(
653     snap: GlobalStateSnapshot,
654     mut original_completion: CompletionItem,
655 ) -> Result<CompletionItem> {
656     let _p = profile::span("handle_completion_resolve");
657
658     if !all_edits_are_disjoint(&original_completion, &[]) {
659         return Err(LspError::new(
660             ErrorCode::InvalidParams as i32,
661             "Received a completion with overlapping edits, this is not LSP-compliant".into(),
662         )
663         .into());
664     }
665
666     // FIXME resolve the other capabilities also?
667     let completion_config = &snap.config.completion();
668     if !completion_config
669         .active_resolve_capabilities
670         .contains(&CompletionResolveCapability::AdditionalTextEdits)
671     {
672         return Ok(original_completion);
673     }
674
675     let resolve_data = match original_completion
676         .data
677         .take()
678         .map(|data| serde_json::from_value::<CompletionResolveData>(data))
679         .transpose()?
680     {
681         Some(data) => data,
682         None => return Ok(original_completion),
683     };
684
685     let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
686     let line_index = snap.analysis.file_line_index(file_id)?;
687     let line_endings = snap.file_line_endings(file_id);
688     let offset = from_proto::offset(&line_index, resolve_data.position.position);
689
690     let additional_edits = snap
691         .analysis
692         .resolve_completion_edits(
693             &completion_config,
694             FilePosition { file_id, offset },
695             &resolve_data.full_import_path,
696             resolve_data.imported_name,
697         )?
698         .into_iter()
699         .flat_map(|edit| {
700             edit.into_iter().map(|indel| to_proto::text_edit(&line_index, line_endings, indel))
701         })
702         .collect_vec();
703
704     if !all_edits_are_disjoint(&original_completion, &additional_edits) {
705         return Err(LspError::new(
706             ErrorCode::InternalError as i32,
707             "Import edit overlaps with the original completion edits, this is not LSP-compliant"
708                 .into(),
709         )
710         .into());
711     }
712
713     if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
714         original_additional_edits.extend(additional_edits.into_iter())
715     } else {
716         original_completion.additional_text_edits = Some(additional_edits);
717     }
718
719     Ok(original_completion)
720 }
721
722 pub(crate) fn handle_folding_range(
723     snap: GlobalStateSnapshot,
724     params: FoldingRangeParams,
725 ) -> Result<Option<Vec<FoldingRange>>> {
726     let _p = profile::span("handle_folding_range");
727     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
728     let folds = snap.analysis.folding_ranges(file_id)?;
729     let text = snap.analysis.file_text(file_id)?;
730     let line_index = snap.analysis.file_line_index(file_id)?;
731     let line_folding_only = snap.config.line_folding_only();
732     let res = folds
733         .into_iter()
734         .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
735         .collect();
736     Ok(Some(res))
737 }
738
739 pub(crate) fn handle_signature_help(
740     snap: GlobalStateSnapshot,
741     params: lsp_types::SignatureHelpParams,
742 ) -> Result<Option<lsp_types::SignatureHelp>> {
743     let _p = profile::span("handle_signature_help");
744     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
745     let call_info = match snap.analysis.call_info(position)? {
746         Some(it) => it,
747         None => return Ok(None),
748     };
749     let concise = !snap.config.call_info_full();
750     let res =
751         to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
752     Ok(Some(res))
753 }
754
755 pub(crate) fn handle_hover(
756     snap: GlobalStateSnapshot,
757     params: lsp_types::HoverParams,
758 ) -> Result<Option<lsp_ext::Hover>> {
759     let _p = profile::span("handle_hover");
760     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
761     let hover_config = snap.config.hover();
762     let info =
763         match snap.analysis.hover(position, hover_config.links_in_hover, hover_config.markdown)? {
764             None => return Ok(None),
765             Some(info) => info,
766         };
767     let line_index = snap.analysis.file_line_index(position.file_id)?;
768     let range = to_proto::range(&line_index, info.range);
769     let hover = lsp_ext::Hover {
770         hover: lsp_types::Hover {
771             contents: HoverContents::Markup(to_proto::markup_content(info.info.markup)),
772             range: Some(range),
773         },
774         actions: prepare_hover_actions(&snap, position.file_id, &info.info.actions),
775     };
776
777     Ok(Some(hover))
778 }
779
780 pub(crate) fn handle_prepare_rename(
781     snap: GlobalStateSnapshot,
782     params: lsp_types::TextDocumentPositionParams,
783 ) -> Result<Option<PrepareRenameResponse>> {
784     let _p = profile::span("handle_prepare_rename");
785     let position = from_proto::file_position(&snap, params)?;
786
787     let change = snap.analysis.prepare_rename(position)??;
788     let line_index = snap.analysis.file_line_index(position.file_id)?;
789     let range = to_proto::range(&line_index, change.range);
790     Ok(Some(PrepareRenameResponse::Range(range)))
791 }
792
793 pub(crate) fn handle_rename(
794     snap: GlobalStateSnapshot,
795     params: RenameParams,
796 ) -> Result<Option<WorkspaceEdit>> {
797     let _p = profile::span("handle_rename");
798     let position = from_proto::file_position(&snap, params.text_document_position)?;
799
800     if params.new_name.is_empty() {
801         return Err(LspError::new(
802             ErrorCode::InvalidParams as i32,
803             "New Name cannot be empty".into(),
804         )
805         .into());
806     }
807
808     let change = snap.analysis.rename(position, &*params.new_name)??;
809     let workspace_edit = to_proto::workspace_edit(&snap, change.info)?;
810     Ok(Some(workspace_edit))
811 }
812
813 pub(crate) fn handle_references(
814     snap: GlobalStateSnapshot,
815     params: lsp_types::ReferenceParams,
816 ) -> Result<Option<Vec<Location>>> {
817     let _p = profile::span("handle_references");
818     let position = from_proto::file_position(&snap, params.text_document_position)?;
819
820     let refs = match snap.analysis.find_all_refs(position, None)? {
821         None => return Ok(None),
822         Some(refs) => refs,
823     };
824
825     let locations = if params.context.include_declaration {
826         refs.into_iter()
827             .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
828             .collect()
829     } else {
830         // Only iterate over the references if include_declaration was false
831         refs.references()
832             .iter()
833             .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
834             .collect()
835     };
836
837     Ok(Some(locations))
838 }
839
840 pub(crate) fn handle_formatting(
841     snap: GlobalStateSnapshot,
842     params: DocumentFormattingParams,
843 ) -> Result<Option<Vec<lsp_types::TextEdit>>> {
844     let _p = profile::span("handle_formatting");
845     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
846     let file = snap.analysis.file_text(file_id)?;
847     let crate_ids = snap.analysis.crate_for(file_id)?;
848
849     let file_line_index = snap.analysis.file_line_index(file_id)?;
850     let file_line_endings = snap.file_line_endings(file_id);
851
852     let mut rustfmt = match snap.config.rustfmt() {
853         RustfmtConfig::Rustfmt { extra_args } => {
854             let mut cmd = process::Command::new(toolchain::rustfmt());
855             cmd.args(extra_args);
856             if let Some(&crate_id) = crate_ids.first() {
857                 // Assume all crates are in the same edition
858                 let edition = snap.analysis.crate_edition(crate_id)?;
859                 cmd.arg("--edition");
860                 cmd.arg(edition.to_string());
861             }
862             cmd
863         }
864         RustfmtConfig::CustomCommand { command, args } => {
865             let mut cmd = process::Command::new(command);
866             cmd.args(args);
867             cmd
868         }
869     };
870
871     let mut rustfmt =
872         rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
873
874     rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
875
876     let output = rustfmt.wait_with_output()?;
877     let captured_stdout = String::from_utf8(output.stdout)?;
878     let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
879
880     if !output.status.success() {
881         match output.status.code() {
882             Some(1) if !captured_stderr.contains("not installed") => {
883                 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
884                 // likely cause exiting with 1. Most Language Servers swallow parse errors on
885                 // formatting because otherwise an error is surfaced to the user on top of the
886                 // syntax error diagnostics they're already receiving. This is especially jarring
887                 // if they have format on save enabled.
888                 log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
889                 return Ok(None);
890             }
891             _ => {
892                 // Something else happened - e.g. `rustfmt` is missing or caught a signal
893                 return Err(LspError::new(
894                     -32900,
895                     format!(
896                         r#"rustfmt exited with:
897                            Status: {}
898                            stdout: {}
899                            stderr: {}"#,
900                         output.status, captured_stdout, captured_stderr,
901                     ),
902                 )
903                 .into());
904             }
905         }
906     }
907
908     let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
909
910     if file_line_endings != new_line_endings {
911         // If line endings are different, send the entire file.
912         // Diffing would not work here, as the line endings might be the only
913         // difference.
914         Ok(Some(to_proto::text_edit_vec(
915             &file_line_index,
916             new_line_endings,
917             TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
918         )))
919     } else if *file == new_text {
920         // The document is already formatted correctly -- no edits needed.
921         Ok(None)
922     } else {
923         Ok(Some(to_proto::text_edit_vec(
924             &file_line_index,
925             file_line_endings,
926             diff(&file, &new_text),
927         )))
928     }
929 }
930
931 pub(crate) fn handle_code_action(
932     snap: GlobalStateSnapshot,
933     params: lsp_types::CodeActionParams,
934 ) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
935     let _p = profile::span("handle_code_action");
936     // We intentionally don't support command-based actions, as those either
937     // requires custom client-code anyway, or requires server-initiated edits.
938     // Server initiated edits break causality, so we avoid those as well.
939     if !snap.config.code_action_literals() {
940         return Ok(None);
941     }
942
943     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
944     let line_index = snap.analysis.file_line_index(file_id)?;
945     let range = from_proto::text_range(&line_index, params.range);
946     let frange = FileRange { file_id, range };
947
948     let mut assists_config = snap.config.assist();
949     assists_config.allowed = params
950         .clone()
951         .context
952         .only
953         .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
954
955     let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
956
957     let include_quick_fixes = match &params.context.only {
958         Some(v) => v.iter().any(|it| {
959             it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX
960         }),
961         None => true,
962     };
963     if include_quick_fixes {
964         add_quick_fixes(&snap, frange, &line_index, &mut res)?;
965     }
966
967     if snap.config.code_action_resolve() {
968         for (index, assist) in
969             snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
970         {
971             res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
972         }
973     } else {
974         for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() {
975             res.push(to_proto::resolved_code_action(&snap, assist)?);
976         }
977     }
978
979     Ok(Some(res))
980 }
981
982 fn add_quick_fixes(
983     snap: &GlobalStateSnapshot,
984     frange: FileRange,
985     line_index: &Arc<LineIndex>,
986     acc: &mut Vec<lsp_ext::CodeAction>,
987 ) -> Result<()> {
988     let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?;
989
990     for fix in diagnostics
991         .into_iter()
992         .filter_map(|d| d.fix)
993         .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some())
994     {
995         let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
996         let action = lsp_ext::CodeAction {
997             title: fix.label.to_string(),
998             group: None,
999             kind: Some(CodeActionKind::QUICKFIX),
1000             edit: Some(edit),
1001             is_preferred: Some(false),
1002             data: None,
1003         };
1004         acc.push(action);
1005     }
1006
1007     for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
1008         let fix_range = from_proto::text_range(&line_index, fix.range);
1009         if fix_range.intersect(frange.range).is_some() {
1010             acc.push(fix.action.clone());
1011         }
1012     }
1013     Ok(())
1014 }
1015
1016 pub(crate) fn handle_code_action_resolve(
1017     snap: GlobalStateSnapshot,
1018     mut code_action: lsp_ext::CodeAction,
1019 ) -> Result<lsp_ext::CodeAction> {
1020     let _p = profile::span("handle_code_action_resolve");
1021     let params = match code_action.data.take() {
1022         Some(it) => it,
1023         None => Err("can't resolve code action without data")?,
1024     };
1025
1026     let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
1027     let line_index = snap.analysis.file_line_index(file_id)?;
1028     let range = from_proto::text_range(&line_index, params.code_action_params.range);
1029     let frange = FileRange { file_id, range };
1030
1031     let mut assists_config = snap.config.assist();
1032     assists_config.allowed = params
1033         .code_action_params
1034         .context
1035         .only
1036         .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
1037
1038     let assists = snap.analysis.assists(&assists_config, true, frange)?;
1039     let (id, index) = split_once(&params.id, ':').unwrap();
1040     let index = index.parse::<usize>().unwrap();
1041     let assist = &assists[index];
1042     assert!(assist.id.0 == id);
1043     let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
1044     code_action.edit = edit;
1045     Ok(code_action)
1046 }
1047
1048 pub(crate) fn handle_code_lens(
1049     snap: GlobalStateSnapshot,
1050     params: lsp_types::CodeLensParams,
1051 ) -> Result<Option<Vec<CodeLens>>> {
1052     let _p = profile::span("handle_code_lens");
1053     let mut lenses: Vec<CodeLens> = Default::default();
1054
1055     let lens_config = snap.config.lens();
1056     if lens_config.none() {
1057         // early return before any db query!
1058         return Ok(Some(lenses));
1059     }
1060
1061     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1062     let line_index = snap.analysis.file_line_index(file_id)?;
1063     let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1064
1065     if lens_config.runnable() {
1066         // Gather runnables
1067         for runnable in snap.analysis.runnables(file_id)? {
1068             if should_skip_target(&runnable, cargo_spec.as_ref()) {
1069                 continue;
1070             }
1071
1072             let action = runnable.action();
1073             let range = to_proto::range(&line_index, runnable.nav.full_range);
1074             let r = to_proto::runnable(&snap, file_id, runnable)?;
1075             if lens_config.run {
1076                 let lens = CodeLens {
1077                     range,
1078                     command: Some(run_single_command(&r, action.run_title)),
1079                     data: None,
1080                 };
1081                 lenses.push(lens);
1082             }
1083
1084             if action.debugee && lens_config.debug {
1085                 let debug_lens =
1086                     CodeLens { range, command: Some(debug_single_command(&r)), data: None };
1087                 lenses.push(debug_lens);
1088             }
1089         }
1090     }
1091
1092     if lens_config.implementations {
1093         // Handle impls
1094         lenses.extend(
1095             snap.analysis
1096                 .file_structure(file_id)?
1097                 .into_iter()
1098                 .filter(|it| {
1099                     matches!(
1100                         it.kind,
1101                         SymbolKind::Trait
1102                             | SymbolKind::Struct
1103                             | SymbolKind::Enum
1104                             | SymbolKind::Union
1105                     )
1106                 })
1107                 .map(|it| {
1108                     let range = to_proto::range(&line_index, it.node_range);
1109                     let pos = range.start;
1110                     let lens_params = lsp_types::request::GotoImplementationParams {
1111                         text_document_position_params: lsp_types::TextDocumentPositionParams::new(
1112                             params.text_document.clone(),
1113                             pos,
1114                         ),
1115                         work_done_progress_params: Default::default(),
1116                         partial_result_params: Default::default(),
1117                     };
1118                     CodeLens {
1119                         range,
1120                         command: None,
1121                         data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()),
1122                     }
1123                 }),
1124         );
1125     }
1126
1127     if lens_config.references() {
1128         lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| {
1129             let range = to_proto::range(&line_index, it.range);
1130             let position = to_proto::position(&line_index, it.range.start());
1131             let lens_params =
1132                 lsp_types::TextDocumentPositionParams::new(params.text_document.clone(), position);
1133
1134             CodeLens {
1135                 range,
1136                 command: None,
1137                 data: Some(to_value(CodeLensResolveData::References(lens_params)).unwrap()),
1138             }
1139         }));
1140     }
1141
1142     Ok(Some(lenses))
1143 }
1144
1145 #[derive(Debug, Serialize, Deserialize)]
1146 #[serde(rename_all = "camelCase")]
1147 enum CodeLensResolveData {
1148     Impls(lsp_types::request::GotoImplementationParams),
1149     References(lsp_types::TextDocumentPositionParams),
1150 }
1151
1152 pub(crate) fn handle_code_lens_resolve(
1153     snap: GlobalStateSnapshot,
1154     code_lens: CodeLens,
1155 ) -> Result<CodeLens> {
1156     let _p = profile::span("handle_code_lens_resolve");
1157     let data = code_lens.data.unwrap();
1158     let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
1159     match resolve {
1160         Some(CodeLensResolveData::Impls(lens_params)) => {
1161             let locations: Vec<Location> =
1162                 match handle_goto_implementation(snap, lens_params.clone())? {
1163                     Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
1164                     Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
1165                     Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
1166                         .into_iter()
1167                         .map(|link| Location::new(link.target_uri, link.target_selection_range))
1168                         .collect(),
1169                     _ => vec![],
1170                 };
1171
1172             let title = implementation_title(locations.len());
1173             let cmd = show_references_command(
1174                 title,
1175                 &lens_params.text_document_position_params.text_document.uri,
1176                 code_lens.range.start,
1177                 locations,
1178             );
1179             Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
1180         }
1181         Some(CodeLensResolveData::References(doc_position)) => {
1182             let position = from_proto::file_position(&snap, doc_position.clone())?;
1183             let locations = snap
1184                 .analysis
1185                 .find_all_refs(position, None)
1186                 .unwrap_or(None)
1187                 .map(|r| {
1188                     r.references()
1189                         .iter()
1190                         .filter_map(|it| to_proto::location(&snap, it.file_range).ok())
1191                         .collect_vec()
1192                 })
1193                 .unwrap_or_default();
1194
1195             let title = reference_title(locations.len());
1196             let cmd = if locations.is_empty() {
1197                 Command { title, command: "".into(), arguments: None }
1198             } else {
1199                 show_references_command(
1200                     title,
1201                     &doc_position.text_document.uri,
1202                     code_lens.range.start,
1203                     locations,
1204                 )
1205             };
1206
1207             Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
1208         }
1209         None => Ok(CodeLens {
1210             range: code_lens.range,
1211             command: Some(Command { title: "Error".into(), ..Default::default() }),
1212             data: None,
1213         }),
1214     }
1215 }
1216
1217 pub(crate) fn handle_document_highlight(
1218     snap: GlobalStateSnapshot,
1219     params: lsp_types::DocumentHighlightParams,
1220 ) -> Result<Option<Vec<DocumentHighlight>>> {
1221     let _p = profile::span("handle_document_highlight");
1222     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1223     let line_index = snap.analysis.file_line_index(position.file_id)?;
1224
1225     let refs = match snap
1226         .analysis
1227         .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
1228     {
1229         None => return Ok(None),
1230         Some(refs) => refs,
1231     };
1232
1233     let res = refs
1234         .into_iter()
1235         .filter(|reference| reference.file_range.file_id == position.file_id)
1236         .map(|reference| DocumentHighlight {
1237             range: to_proto::range(&line_index, reference.file_range.range),
1238             kind: reference.access.map(to_proto::document_highlight_kind),
1239         })
1240         .collect();
1241     Ok(Some(res))
1242 }
1243
1244 pub(crate) fn handle_ssr(
1245     snap: GlobalStateSnapshot,
1246     params: lsp_ext::SsrParams,
1247 ) -> Result<lsp_types::WorkspaceEdit> {
1248     let _p = profile::span("handle_ssr");
1249     let selections = params
1250         .selections
1251         .iter()
1252         .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
1253         .collect::<Result<Vec<_>, _>>()?;
1254     let position = from_proto::file_position(&snap, params.position)?;
1255     let source_change = snap.analysis.structural_search_replace(
1256         &params.query,
1257         params.parse_only,
1258         position,
1259         selections,
1260     )??;
1261     to_proto::workspace_edit(&snap, source_change)
1262 }
1263
1264 pub(crate) fn publish_diagnostics(
1265     snap: &GlobalStateSnapshot,
1266     file_id: FileId,
1267 ) -> Result<Vec<Diagnostic>> {
1268     let _p = profile::span("publish_diagnostics");
1269     let line_index = snap.analysis.file_line_index(file_id)?;
1270
1271     let diagnostics: Vec<Diagnostic> = snap
1272         .analysis
1273         .diagnostics(&snap.config.diagnostics(), file_id)?
1274         .into_iter()
1275         .map(|d| Diagnostic {
1276             range: to_proto::range(&line_index, d.range),
1277             severity: Some(to_proto::diagnostic_severity(d.severity)),
1278             code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String),
1279             code_description: d.code.and_then(|code| {
1280                 lsp_types::Url::parse(&format!(
1281                     "https://rust-analyzer.github.io/manual.html#{}",
1282                     code.as_str()
1283                 ))
1284                 .ok()
1285                 .map(|href| lsp_types::CodeDescription { href })
1286             }),
1287             source: Some("rust-analyzer".to_string()),
1288             message: d.message,
1289             related_information: None,
1290             tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
1291             data: None,
1292         })
1293         .collect();
1294     Ok(diagnostics)
1295 }
1296
1297 pub(crate) fn handle_inlay_hints(
1298     snap: GlobalStateSnapshot,
1299     params: InlayHintsParams,
1300 ) -> Result<Vec<InlayHint>> {
1301     let _p = profile::span("handle_inlay_hints");
1302     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1303     let line_index = snap.analysis.file_line_index(file_id)?;
1304     Ok(snap
1305         .analysis
1306         .inlay_hints(file_id, &snap.config.inlay_hints())?
1307         .into_iter()
1308         .map(|it| to_proto::inlay_hint(&line_index, it))
1309         .collect())
1310 }
1311
1312 pub(crate) fn handle_call_hierarchy_prepare(
1313     snap: GlobalStateSnapshot,
1314     params: CallHierarchyPrepareParams,
1315 ) -> Result<Option<Vec<CallHierarchyItem>>> {
1316     let _p = profile::span("handle_call_hierarchy_prepare");
1317     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1318
1319     let nav_info = match snap.analysis.call_hierarchy(position)? {
1320         None => return Ok(None),
1321         Some(it) => it,
1322     };
1323
1324     let RangeInfo { range: _, info: navs } = nav_info;
1325     let res = navs
1326         .into_iter()
1327         .filter(|it| it.kind == Some(SymbolKind::Function))
1328         .map(|it| to_proto::call_hierarchy_item(&snap, it))
1329         .collect::<Result<Vec<_>>>()?;
1330
1331     Ok(Some(res))
1332 }
1333
1334 pub(crate) fn handle_call_hierarchy_incoming(
1335     snap: GlobalStateSnapshot,
1336     params: CallHierarchyIncomingCallsParams,
1337 ) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1338     let _p = profile::span("handle_call_hierarchy_incoming");
1339     let item = params.item;
1340
1341     let doc = TextDocumentIdentifier::new(item.uri);
1342     let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1343     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1344
1345     let call_items = match snap.analysis.incoming_calls(fpos)? {
1346         None => return Ok(None),
1347         Some(it) => it,
1348     };
1349
1350     let mut res = vec![];
1351
1352     for call_item in call_items.into_iter() {
1353         let file_id = call_item.target.file_id;
1354         let line_index = snap.analysis.file_line_index(file_id)?;
1355         let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1356         res.push(CallHierarchyIncomingCall {
1357             from: item,
1358             from_ranges: call_item
1359                 .ranges
1360                 .into_iter()
1361                 .map(|it| to_proto::range(&line_index, it))
1362                 .collect(),
1363         });
1364     }
1365
1366     Ok(Some(res))
1367 }
1368
1369 pub(crate) fn handle_call_hierarchy_outgoing(
1370     snap: GlobalStateSnapshot,
1371     params: CallHierarchyOutgoingCallsParams,
1372 ) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1373     let _p = profile::span("handle_call_hierarchy_outgoing");
1374     let item = params.item;
1375
1376     let doc = TextDocumentIdentifier::new(item.uri);
1377     let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1378     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1379
1380     let call_items = match snap.analysis.outgoing_calls(fpos)? {
1381         None => return Ok(None),
1382         Some(it) => it,
1383     };
1384
1385     let mut res = vec![];
1386
1387     for call_item in call_items.into_iter() {
1388         let file_id = call_item.target.file_id;
1389         let line_index = snap.analysis.file_line_index(file_id)?;
1390         let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1391         res.push(CallHierarchyOutgoingCall {
1392             to: item,
1393             from_ranges: call_item
1394                 .ranges
1395                 .into_iter()
1396                 .map(|it| to_proto::range(&line_index, it))
1397                 .collect(),
1398         });
1399     }
1400
1401     Ok(Some(res))
1402 }
1403
1404 pub(crate) fn handle_semantic_tokens_full(
1405     snap: GlobalStateSnapshot,
1406     params: SemanticTokensParams,
1407 ) -> Result<Option<SemanticTokensResult>> {
1408     let _p = profile::span("handle_semantic_tokens_full");
1409
1410     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1411     let text = snap.analysis.file_text(file_id)?;
1412     let line_index = snap.analysis.file_line_index(file_id)?;
1413
1414     let highlights = snap.analysis.highlight(file_id)?;
1415     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1416
1417     // Unconditionally cache the tokens
1418     snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
1419
1420     Ok(Some(semantic_tokens.into()))
1421 }
1422
1423 pub(crate) fn handle_semantic_tokens_full_delta(
1424     snap: GlobalStateSnapshot,
1425     params: SemanticTokensDeltaParams,
1426 ) -> Result<Option<SemanticTokensFullDeltaResult>> {
1427     let _p = profile::span("handle_semantic_tokens_full_delta");
1428
1429     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1430     let text = snap.analysis.file_text(file_id)?;
1431     let line_index = snap.analysis.file_line_index(file_id)?;
1432
1433     let highlights = snap.analysis.highlight(file_id)?;
1434
1435     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1436
1437     let mut cache = snap.semantic_tokens_cache.lock();
1438     let cached_tokens = cache.entry(params.text_document.uri).or_default();
1439
1440     if let Some(prev_id) = &cached_tokens.result_id {
1441         if *prev_id == params.previous_result_id {
1442             let delta = to_proto::semantic_token_delta(&cached_tokens, &semantic_tokens);
1443             *cached_tokens = semantic_tokens;
1444             return Ok(Some(delta.into()));
1445         }
1446     }
1447
1448     *cached_tokens = semantic_tokens.clone();
1449
1450     Ok(Some(semantic_tokens.into()))
1451 }
1452
1453 pub(crate) fn handle_semantic_tokens_range(
1454     snap: GlobalStateSnapshot,
1455     params: SemanticTokensRangeParams,
1456 ) -> Result<Option<SemanticTokensRangeResult>> {
1457     let _p = profile::span("handle_semantic_tokens_range");
1458
1459     let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1460     let text = snap.analysis.file_text(frange.file_id)?;
1461     let line_index = snap.analysis.file_line_index(frange.file_id)?;
1462
1463     let highlights = snap.analysis.highlight_range(frange)?;
1464     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1465     Ok(Some(semantic_tokens.into()))
1466 }
1467
1468 pub(crate) fn handle_open_docs(
1469     snap: GlobalStateSnapshot,
1470     params: lsp_types::TextDocumentPositionParams,
1471 ) -> Result<Option<lsp_types::Url>> {
1472     let _p = profile::span("handle_open_docs");
1473     let position = from_proto::file_position(&snap, params)?;
1474
1475     let remote = snap.analysis.external_docs(position)?;
1476
1477     Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
1478 }
1479
1480 pub(crate) fn handle_open_cargo_toml(
1481     snap: GlobalStateSnapshot,
1482     params: lsp_ext::OpenCargoTomlParams,
1483 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
1484     let _p = profile::span("handle_open_cargo_toml");
1485     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1486
1487     let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
1488         Some(it) => it,
1489         None => return Ok(None),
1490     };
1491
1492     let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
1493     let res: lsp_types::GotoDefinitionResponse =
1494         Location::new(cargo_toml_url, Range::default()).into();
1495     Ok(Some(res))
1496 }
1497
1498 fn implementation_title(count: usize) -> String {
1499     if count == 1 {
1500         "1 implementation".into()
1501     } else {
1502         format!("{} implementations", count)
1503     }
1504 }
1505
1506 fn reference_title(count: usize) -> String {
1507     if count == 1 {
1508         "1 reference".into()
1509     } else {
1510         format!("{} references", count)
1511     }
1512 }
1513
1514 fn show_references_command(
1515     title: String,
1516     uri: &lsp_types::Url,
1517     position: lsp_types::Position,
1518     locations: Vec<lsp_types::Location>,
1519 ) -> Command {
1520     // We cannot use the 'editor.action.showReferences' command directly
1521     // because that command requires vscode types which we convert in the handler
1522     // on the client side.
1523
1524     Command {
1525         title,
1526         command: "rust-analyzer.showReferences".into(),
1527         arguments: Some(vec![
1528             to_value(uri).unwrap(),
1529             to_value(position).unwrap(),
1530             to_value(locations).unwrap(),
1531         ]),
1532     }
1533 }
1534
1535 fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1536     Command {
1537         title: title.to_string(),
1538         command: "rust-analyzer.runSingle".into(),
1539         arguments: Some(vec![to_value(runnable).unwrap()]),
1540     }
1541 }
1542
1543 fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1544     Command {
1545         title: "Debug".into(),
1546         command: "rust-analyzer.debugSingle".into(),
1547         arguments: Some(vec![to_value(runnable).unwrap()]),
1548     }
1549 }
1550
1551 fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1552     let value = if snap.config.location_link() {
1553         let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1554         to_value(link).ok()?
1555     } else {
1556         let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
1557         let location = to_proto::location(snap, range).ok()?;
1558         to_value(location).ok()?
1559     };
1560
1561     Some(Command {
1562         title: nav.name.to_string(),
1563         command: "rust-analyzer.gotoLocation".into(),
1564         arguments: Some(vec![value]),
1565     })
1566 }
1567
1568 fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1569     lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1570 }
1571
1572 fn show_impl_command_link(
1573     snap: &GlobalStateSnapshot,
1574     position: &FilePosition,
1575 ) -> Option<lsp_ext::CommandLinkGroup> {
1576     if snap.config.hover().implementations {
1577         if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
1578             let uri = to_proto::url(snap, position.file_id);
1579             let line_index = snap.analysis.file_line_index(position.file_id).ok()?;
1580             let position = to_proto::position(&line_index, position.offset);
1581             let locations: Vec<_> = nav_data
1582                 .info
1583                 .into_iter()
1584                 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1585                 .collect();
1586             let title = implementation_title(locations.len());
1587             let command = show_references_command(title, &uri, position, locations);
1588
1589             return Some(lsp_ext::CommandLinkGroup {
1590                 commands: vec![to_command_link(command, "Go to implementations".into())],
1591                 ..Default::default()
1592             });
1593         }
1594     }
1595     None
1596 }
1597
1598 fn runnable_action_links(
1599     snap: &GlobalStateSnapshot,
1600     file_id: FileId,
1601     runnable: Runnable,
1602 ) -> Option<lsp_ext::CommandLinkGroup> {
1603     let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1604     let hover_config = snap.config.hover();
1605     if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1606         return None;
1607     }
1608
1609     let action: &'static _ = runnable.action();
1610     to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1611         let mut group = lsp_ext::CommandLinkGroup::default();
1612
1613         if hover_config.run {
1614             let run_command = run_single_command(&r, action.run_title);
1615             group.commands.push(to_command_link(run_command, r.label.clone()));
1616         }
1617
1618         if hover_config.debug {
1619             let dbg_command = debug_single_command(&r);
1620             group.commands.push(to_command_link(dbg_command, r.label));
1621         }
1622
1623         group
1624     })
1625 }
1626
1627 fn goto_type_action_links(
1628     snap: &GlobalStateSnapshot,
1629     nav_targets: &[HoverGotoTypeData],
1630 ) -> Option<lsp_ext::CommandLinkGroup> {
1631     if !snap.config.hover().goto_type_def || nav_targets.is_empty() {
1632         return None;
1633     }
1634
1635     Some(lsp_ext::CommandLinkGroup {
1636         title: Some("Go to ".into()),
1637         commands: nav_targets
1638             .iter()
1639             .filter_map(|it| {
1640                 goto_location_command(snap, &it.nav)
1641                     .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
1642             })
1643             .collect(),
1644     })
1645 }
1646
1647 fn prepare_hover_actions(
1648     snap: &GlobalStateSnapshot,
1649     file_id: FileId,
1650     actions: &[HoverAction],
1651 ) -> Vec<lsp_ext::CommandLinkGroup> {
1652     if snap.config.hover().none() || !snap.config.hover_actions() {
1653         return Vec::new();
1654     }
1655
1656     actions
1657         .iter()
1658         .filter_map(|it| match it {
1659             HoverAction::Implementation(position) => show_impl_command_link(snap, position),
1660             HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
1661             HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1662         })
1663         .collect()
1664 }
1665
1666 fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
1667     match runnable.kind {
1668         RunnableKind::Bin => {
1669             // Do not suggest binary run on other target than binary
1670             match &cargo_spec {
1671                 Some(spec) => !matches!(
1672                     spec.target_kind,
1673                     TargetKind::Bin | TargetKind::Example | TargetKind::Test
1674                 ),
1675                 None => true,
1676             }
1677         }
1678         _ => false,
1679     }
1680 }
1681
1682 #[derive(Debug, Serialize, Deserialize)]
1683 struct CompletionResolveData {
1684     position: lsp_types::TextDocumentPositionParams,
1685     full_import_path: String,
1686     imported_name: String,
1687 }
1688
1689 fn fill_resolve_data(
1690     resolve_data: &mut Option<serde_json::Value>,
1691     item: &ide::CompletionItem,
1692     position: &TextDocumentPositionParams,
1693 ) -> Option<()> {
1694     let import_edit = item.import_to_add()?;
1695     let full_import_path = import_edit.import_path.to_string();
1696     let imported_name = import_edit.import_path.segments.clone().pop()?.to_string();
1697
1698     *resolve_data = Some(
1699         to_value(CompletionResolveData {
1700             position: position.to_owned(),
1701             full_import_path,
1702             imported_name,
1703         })
1704         .unwrap(),
1705     );
1706     Some(())
1707 }