]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/handlers.rs
10cbd7eeb95d119497f39829799b6f881494da7d
[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     FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget,
13     Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit,
14 };
15 use ide_db::SymbolKind;
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, FoldingRange, FoldingRangeParams, HoverContents,
23     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 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.into_iter().next().unwrap();
271
272     let change = to_proto::text_edit_vec(&line_index, line_endings, 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<(lsp_types::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 = lsp_types::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: &lsp_types::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                     if from_path.is_dir() {
437                         // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
438                         let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
439                         old_folder_name.push('/');
440                         let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
441
442                         let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
443                         let new_file_name = to_path.file_name()?.to_str()?;
444                         Some((
445                             snap.url_to_file_id(&imitate_from_url).ok()?,
446                             new_file_name.to_string(),
447                         ))
448                     } else {
449                         let old_name = from_path.file_stem()?.to_str()?;
450                         let new_name = to_path.file_stem()?.to_str()?;
451                         match (old_name, new_name) {
452                             ("mod", _) => None,
453                             (_, "mod") => None,
454                             _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
455                         }
456                     }
457                 }
458                 _ => None,
459             }
460         })
461         .filter_map(|(file_id, new_name)| {
462             snap.analysis.will_rename_file(file_id, &new_name).ok()?
463         })
464         .collect();
465
466     // Drop file system edits since we're just renaming things on the same level
467     let mut source_changes = source_changes.into_iter();
468     let mut source_change = source_changes.next().unwrap_or_default();
469     source_change.file_system_edits.clear();
470     // no collect here because we want to merge text edits on same file ids
471     source_change.extend(source_changes.map(|it| it.source_file_edits).flatten());
472     let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
473     Ok(Some(workspace_edit))
474 }
475
476 pub(crate) fn handle_goto_definition(
477     snap: GlobalStateSnapshot,
478     params: lsp_types::GotoDefinitionParams,
479 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
480     let _p = profile::span("handle_goto_definition");
481     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
482     let nav_info = match snap.analysis.goto_definition(position)? {
483         None => return Ok(None),
484         Some(it) => it,
485     };
486     let src = FileRange { file_id: position.file_id, range: nav_info.range };
487     let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
488     Ok(Some(res))
489 }
490
491 pub(crate) fn handle_goto_implementation(
492     snap: GlobalStateSnapshot,
493     params: lsp_types::request::GotoImplementationParams,
494 ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
495     let _p = profile::span("handle_goto_implementation");
496     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
497     let nav_info = match snap.analysis.goto_implementation(position)? {
498         None => return Ok(None),
499         Some(it) => it,
500     };
501     let src = FileRange { file_id: position.file_id, range: nav_info.range };
502     let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
503     Ok(Some(res))
504 }
505
506 pub(crate) fn handle_goto_type_definition(
507     snap: GlobalStateSnapshot,
508     params: lsp_types::request::GotoTypeDefinitionParams,
509 ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
510     let _p = profile::span("handle_goto_type_definition");
511     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
512     let nav_info = match snap.analysis.goto_type_definition(position)? {
513         None => return Ok(None),
514         Some(it) => it,
515     };
516     let src = FileRange { file_id: position.file_id, range: nav_info.range };
517     let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
518     Ok(Some(res))
519 }
520
521 pub(crate) fn handle_parent_module(
522     snap: GlobalStateSnapshot,
523     params: lsp_types::TextDocumentPositionParams,
524 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
525     let _p = profile::span("handle_parent_module");
526     let position = from_proto::file_position(&snap, params)?;
527     let navs = snap.analysis.parent_module(position)?;
528     let res = to_proto::goto_definition_response(&snap, None, navs)?;
529     Ok(Some(res))
530 }
531
532 pub(crate) fn handle_runnables(
533     snap: GlobalStateSnapshot,
534     params: lsp_ext::RunnablesParams,
535 ) -> Result<Vec<lsp_ext::Runnable>> {
536     let _p = profile::span("handle_runnables");
537     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
538     let line_index = snap.analysis.file_line_index(file_id)?;
539     let offset = params.position.map(|it| from_proto::offset(&line_index, it));
540     let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
541
542     let expect_test = match offset {
543         Some(offset) => {
544             let source_file = snap.analysis.parse(file_id)?;
545             algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
546                 .and_then(|it| it.path()?.segment()?.name_ref())
547                 .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
548         }
549         None => false,
550     };
551
552     let mut res = Vec::new();
553     for runnable in snap.analysis.runnables(file_id)? {
554         if let Some(offset) = offset {
555             if !runnable.nav.full_range.contains_inclusive(offset) {
556                 continue;
557             }
558         }
559         if should_skip_target(&runnable, cargo_spec.as_ref()) {
560             continue;
561         }
562         let mut runnable = to_proto::runnable(&snap, file_id, runnable)?;
563         if expect_test {
564             runnable.label = format!("{} + expect", runnable.label);
565             runnable.args.expect_test = Some(true);
566         }
567         res.push(runnable);
568     }
569
570     // Add `cargo check` and `cargo test` for all targets of the whole package
571     let config = snap.config.runnables();
572     match cargo_spec {
573         Some(spec) => {
574             for &cmd in ["check", "test"].iter() {
575                 res.push(lsp_ext::Runnable {
576                     label: format!("cargo {} -p {} --all-targets", cmd, spec.package),
577                     location: None,
578                     kind: lsp_ext::RunnableKind::Cargo,
579                     args: lsp_ext::CargoRunnable {
580                         workspace_root: Some(spec.workspace_root.clone().into()),
581                         override_cargo: config.override_cargo.clone(),
582                         cargo_args: vec![
583                             cmd.to_string(),
584                             "--package".to_string(),
585                             spec.package.clone(),
586                             "--all-targets".to_string(),
587                         ],
588                         cargo_extra_args: config.cargo_extra_args.clone(),
589                         executable_args: Vec::new(),
590                         expect_test: None,
591                     },
592                 })
593             }
594         }
595         None => {
596             res.push(lsp_ext::Runnable {
597                 label: "cargo check --workspace".to_string(),
598                 location: None,
599                 kind: lsp_ext::RunnableKind::Cargo,
600                 args: lsp_ext::CargoRunnable {
601                     workspace_root: None,
602                     override_cargo: config.override_cargo,
603                     cargo_args: vec!["check".to_string(), "--workspace".to_string()],
604                     cargo_extra_args: config.cargo_extra_args,
605                     executable_args: Vec::new(),
606                     expect_test: None,
607                 },
608             });
609         }
610     }
611     Ok(res)
612 }
613
614 pub(crate) fn handle_completion(
615     snap: GlobalStateSnapshot,
616     params: lsp_types::CompletionParams,
617 ) -> Result<Option<lsp_types::CompletionResponse>> {
618     let _p = profile::span("handle_completion");
619     let text_document_position = params.text_document_position.clone();
620     let position = from_proto::file_position(&snap, params.text_document_position)?;
621     let completion_triggered_after_single_colon = {
622         let mut res = false;
623         if let Some(ctx) = params.context {
624             if ctx.trigger_character.unwrap_or_default() == ":" {
625                 let source_file = snap.analysis.parse(position.file_id)?;
626                 let syntax = source_file.syntax();
627                 let text = syntax.text();
628                 if let Some(next_char) = text.char_at(position.offset) {
629                     let diff = TextSize::of(next_char) + TextSize::of(':');
630                     let prev_char = position.offset - diff;
631                     if text.char_at(prev_char) != Some(':') {
632                         res = true;
633                     }
634                 }
635             }
636         }
637         res
638     };
639     if completion_triggered_after_single_colon {
640         return Ok(None);
641     }
642
643     let completion_config = &snap.config.completion();
644     let items = match snap.analysis.completions(completion_config, position)? {
645         None => return Ok(None),
646         Some(items) => items,
647     };
648     let line_index = snap.analysis.file_line_index(position.file_id)?;
649     let line_endings = snap.file_line_endings(position.file_id);
650
651     let items: Vec<CompletionItem> = items
652         .into_iter()
653         .flat_map(|item| {
654             let mut new_completion_items =
655                 to_proto::completion_item(&line_index, line_endings, item.clone());
656
657             if completion_config.enable_imports_on_the_fly {
658                 for new_item in &mut new_completion_items {
659                     fill_resolve_data(&mut new_item.data, &item, &text_document_position);
660                 }
661             }
662
663             new_completion_items
664         })
665         .collect();
666
667     let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
668     Ok(Some(completion_list.into()))
669 }
670
671 pub(crate) fn handle_completion_resolve(
672     snap: GlobalStateSnapshot,
673     mut original_completion: CompletionItem,
674 ) -> Result<CompletionItem> {
675     let _p = profile::span("handle_completion_resolve");
676
677     if !all_edits_are_disjoint(&original_completion, &[]) {
678         return Err(LspError::new(
679             ErrorCode::InvalidParams as i32,
680             "Received a completion with overlapping edits, this is not LSP-compliant".into(),
681         )
682         .into());
683     }
684
685     let resolve_data = match original_completion
686         .data
687         .take()
688         .map(|data| serde_json::from_value::<CompletionResolveData>(data))
689         .transpose()?
690     {
691         Some(data) => data,
692         None => return Ok(original_completion),
693     };
694
695     let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
696     let line_index = snap.analysis.file_line_index(file_id)?;
697     let line_endings = snap.file_line_endings(file_id);
698     let offset = from_proto::offset(&line_index, resolve_data.position.position);
699
700     let additional_edits = snap
701         .analysis
702         .resolve_completion_edits(
703             &snap.config.completion(),
704             FilePosition { file_id, offset },
705             &resolve_data.full_import_path,
706             resolve_data.imported_name,
707             resolve_data.import_for_trait_assoc_item,
708         )?
709         .into_iter()
710         .flat_map(|edit| {
711             edit.into_iter().map(|indel| to_proto::text_edit(&line_index, line_endings, indel))
712         })
713         .collect_vec();
714
715     if !all_edits_are_disjoint(&original_completion, &additional_edits) {
716         return Err(LspError::new(
717             ErrorCode::InternalError as i32,
718             "Import edit overlaps with the original completion edits, this is not LSP-compliant"
719                 .into(),
720         )
721         .into());
722     }
723
724     if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
725         original_additional_edits.extend(additional_edits.into_iter())
726     } else {
727         original_completion.additional_text_edits = Some(additional_edits);
728     }
729
730     Ok(original_completion)
731 }
732
733 pub(crate) fn handle_folding_range(
734     snap: GlobalStateSnapshot,
735     params: FoldingRangeParams,
736 ) -> Result<Option<Vec<FoldingRange>>> {
737     let _p = profile::span("handle_folding_range");
738     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
739     let folds = snap.analysis.folding_ranges(file_id)?;
740     let text = snap.analysis.file_text(file_id)?;
741     let line_index = snap.analysis.file_line_index(file_id)?;
742     let line_folding_only = snap.config.line_folding_only();
743     let res = folds
744         .into_iter()
745         .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
746         .collect();
747     Ok(Some(res))
748 }
749
750 pub(crate) fn handle_signature_help(
751     snap: GlobalStateSnapshot,
752     params: lsp_types::SignatureHelpParams,
753 ) -> Result<Option<lsp_types::SignatureHelp>> {
754     let _p = profile::span("handle_signature_help");
755     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
756     let call_info = match snap.analysis.call_info(position)? {
757         Some(it) => it,
758         None => return Ok(None),
759     };
760     let concise = !snap.config.call_info_full();
761     let res =
762         to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
763     Ok(Some(res))
764 }
765
766 pub(crate) fn handle_hover(
767     snap: GlobalStateSnapshot,
768     params: lsp_types::HoverParams,
769 ) -> Result<Option<lsp_ext::Hover>> {
770     let _p = profile::span("handle_hover");
771     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
772     let hover_config = snap.config.hover();
773     let info =
774         match snap.analysis.hover(position, hover_config.links_in_hover, hover_config.markdown)? {
775             None => return Ok(None),
776             Some(info) => info,
777         };
778     let line_index = snap.analysis.file_line_index(position.file_id)?;
779     let range = to_proto::range(&line_index, info.range);
780     let hover = lsp_ext::Hover {
781         hover: lsp_types::Hover {
782             contents: HoverContents::Markup(to_proto::markup_content(info.info.markup)),
783             range: Some(range),
784         },
785         actions: prepare_hover_actions(&snap, position.file_id, &info.info.actions),
786     };
787
788     Ok(Some(hover))
789 }
790
791 pub(crate) fn handle_prepare_rename(
792     snap: GlobalStateSnapshot,
793     params: lsp_types::TextDocumentPositionParams,
794 ) -> Result<Option<PrepareRenameResponse>> {
795     let _p = profile::span("handle_prepare_rename");
796     let position = from_proto::file_position(&snap, params)?;
797
798     let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
799
800     let line_index = snap.analysis.file_line_index(position.file_id)?;
801     let range = to_proto::range(&line_index, change.range);
802     Ok(Some(PrepareRenameResponse::Range(range)))
803 }
804
805 pub(crate) fn handle_rename(
806     snap: GlobalStateSnapshot,
807     params: RenameParams,
808 ) -> Result<Option<WorkspaceEdit>> {
809     let _p = profile::span("handle_rename");
810     let position = from_proto::file_position(&snap, params.text_document_position)?;
811
812     let change =
813         snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
814     let workspace_edit = to_proto::workspace_edit(&snap, change)?;
815     Ok(Some(workspace_edit))
816 }
817
818 pub(crate) fn handle_references(
819     snap: GlobalStateSnapshot,
820     params: lsp_types::ReferenceParams,
821 ) -> Result<Option<Vec<Location>>> {
822     let _p = profile::span("handle_references");
823     let position = from_proto::file_position(&snap, params.text_document_position)?;
824
825     let refs = match snap.analysis.find_all_refs(position, None)? {
826         None => return Ok(None),
827         Some(refs) => refs,
828     };
829
830     let locations = if params.context.include_declaration {
831         refs.references_with_declaration()
832             .file_ranges()
833             .filter_map(|frange| to_proto::location(&snap, frange).ok())
834             .collect()
835     } else {
836         // Only iterate over the references if include_declaration was false
837         refs.references()
838             .file_ranges()
839             .filter_map(|frange| to_proto::location(&snap, frange).ok())
840             .collect()
841     };
842
843     Ok(Some(locations))
844 }
845
846 pub(crate) fn handle_formatting(
847     snap: GlobalStateSnapshot,
848     params: DocumentFormattingParams,
849 ) -> Result<Option<Vec<lsp_types::TextEdit>>> {
850     let _p = profile::span("handle_formatting");
851     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
852     let file = snap.analysis.file_text(file_id)?;
853     let crate_ids = snap.analysis.crate_for(file_id)?;
854
855     let file_line_index = snap.analysis.file_line_index(file_id)?;
856     let file_line_endings = snap.file_line_endings(file_id);
857
858     let mut rustfmt = match snap.config.rustfmt() {
859         RustfmtConfig::Rustfmt { extra_args } => {
860             let mut cmd = process::Command::new(toolchain::rustfmt());
861             cmd.args(extra_args);
862             if let Some(&crate_id) = crate_ids.first() {
863                 // Assume all crates are in the same edition
864                 let edition = snap.analysis.crate_edition(crate_id)?;
865                 cmd.arg("--edition");
866                 cmd.arg(edition.to_string());
867             }
868             cmd
869         }
870         RustfmtConfig::CustomCommand { command, args } => {
871             let mut cmd = process::Command::new(command);
872             cmd.args(args);
873             cmd
874         }
875     };
876
877     let mut rustfmt =
878         rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
879
880     rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
881
882     let output = rustfmt.wait_with_output()?;
883     let captured_stdout = String::from_utf8(output.stdout)?;
884     let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
885
886     if !output.status.success() {
887         match output.status.code() {
888             Some(1) if !captured_stderr.contains("not installed") => {
889                 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
890                 // likely cause exiting with 1. Most Language Servers swallow parse errors on
891                 // formatting because otherwise an error is surfaced to the user on top of the
892                 // syntax error diagnostics they're already receiving. This is especially jarring
893                 // if they have format on save enabled.
894                 log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
895                 return Ok(None);
896             }
897             _ => {
898                 // Something else happened - e.g. `rustfmt` is missing or caught a signal
899                 return Err(LspError::new(
900                     -32900,
901                     format!(
902                         r#"rustfmt exited with:
903                            Status: {}
904                            stdout: {}
905                            stderr: {}"#,
906                         output.status, captured_stdout, captured_stderr,
907                     ),
908                 )
909                 .into());
910             }
911         }
912     }
913
914     let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
915
916     if file_line_endings != new_line_endings {
917         // If line endings are different, send the entire file.
918         // Diffing would not work here, as the line endings might be the only
919         // difference.
920         Ok(Some(to_proto::text_edit_vec(
921             &file_line_index,
922             new_line_endings,
923             TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
924         )))
925     } else if *file == new_text {
926         // The document is already formatted correctly -- no edits needed.
927         Ok(None)
928     } else {
929         Ok(Some(to_proto::text_edit_vec(
930             &file_line_index,
931             file_line_endings,
932             diff(&file, &new_text),
933         )))
934     }
935 }
936
937 pub(crate) fn handle_code_action(
938     snap: GlobalStateSnapshot,
939     params: lsp_types::CodeActionParams,
940 ) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
941     let _p = profile::span("handle_code_action");
942     // We intentionally don't support command-based actions, as those either
943     // requires custom client-code anyway, or requires server-initiated edits.
944     // Server initiated edits break causality, so we avoid those as well.
945     if !snap.config.code_action_literals() {
946         return Ok(None);
947     }
948
949     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
950     let line_index = snap.analysis.file_line_index(file_id)?;
951     let range = from_proto::text_range(&line_index, params.range);
952     let frange = FileRange { file_id, range };
953
954     let mut assists_config = snap.config.assist();
955     assists_config.allowed = params
956         .clone()
957         .context
958         .only
959         .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
960
961     let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
962
963     let include_quick_fixes = match &params.context.only {
964         Some(v) => v.iter().any(|it| {
965             it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX
966         }),
967         None => true,
968     };
969     if include_quick_fixes {
970         add_quick_fixes(&snap, frange, &line_index, &mut res)?;
971     }
972
973     if snap.config.code_action_resolve() {
974         for (index, assist) in
975             snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
976         {
977             res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
978         }
979     } else {
980         for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() {
981             res.push(to_proto::resolved_code_action(&snap, assist)?);
982         }
983     }
984
985     Ok(Some(res))
986 }
987
988 fn add_quick_fixes(
989     snap: &GlobalStateSnapshot,
990     frange: FileRange,
991     line_index: &Arc<LineIndex>,
992     acc: &mut Vec<lsp_ext::CodeAction>,
993 ) -> Result<()> {
994     let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?;
995
996     for fix in diagnostics
997         .into_iter()
998         .filter_map(|d| d.fix)
999         .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some())
1000     {
1001         let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
1002         let action = lsp_ext::CodeAction {
1003             title: fix.label.to_string(),
1004             group: None,
1005             kind: Some(CodeActionKind::QUICKFIX),
1006             edit: Some(edit),
1007             is_preferred: Some(false),
1008             data: None,
1009         };
1010         acc.push(action);
1011     }
1012
1013     for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
1014         let fix_range = from_proto::text_range(&line_index, fix.range);
1015         if fix_range.intersect(frange.range).is_some() {
1016             acc.push(fix.action.clone());
1017         }
1018     }
1019     Ok(())
1020 }
1021
1022 pub(crate) fn handle_code_action_resolve(
1023     snap: GlobalStateSnapshot,
1024     mut code_action: lsp_ext::CodeAction,
1025 ) -> Result<lsp_ext::CodeAction> {
1026     let _p = profile::span("handle_code_action_resolve");
1027     let params = match code_action.data.take() {
1028         Some(it) => it,
1029         None => Err("can't resolve code action without data")?,
1030     };
1031
1032     let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
1033     let line_index = snap.analysis.file_line_index(file_id)?;
1034     let range = from_proto::text_range(&line_index, params.code_action_params.range);
1035     let frange = FileRange { file_id, range };
1036
1037     let mut assists_config = snap.config.assist();
1038     assists_config.allowed = params
1039         .code_action_params
1040         .context
1041         .only
1042         .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
1043
1044     let assists = snap.analysis.assists(&assists_config, true, frange)?;
1045     let (id, index) = split_once(&params.id, ':').unwrap();
1046     let index = index.parse::<usize>().unwrap();
1047     let assist = &assists[index];
1048     assert!(assist.id.0 == id);
1049     let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
1050     code_action.edit = edit;
1051     Ok(code_action)
1052 }
1053
1054 pub(crate) fn handle_code_lens(
1055     snap: GlobalStateSnapshot,
1056     params: lsp_types::CodeLensParams,
1057 ) -> Result<Option<Vec<CodeLens>>> {
1058     let _p = profile::span("handle_code_lens");
1059     let mut lenses: Vec<CodeLens> = Default::default();
1060
1061     let lens_config = snap.config.lens();
1062     if lens_config.none() {
1063         // early return before any db query!
1064         return Ok(Some(lenses));
1065     }
1066
1067     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1068     let line_index = snap.analysis.file_line_index(file_id)?;
1069     let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1070
1071     if lens_config.runnable() {
1072         // Gather runnables
1073         for runnable in snap.analysis.runnables(file_id)? {
1074             if should_skip_target(&runnable, cargo_spec.as_ref()) {
1075                 continue;
1076             }
1077
1078             let action = runnable.action();
1079             let range = to_proto::range(&line_index, runnable.nav.full_range);
1080             let r = to_proto::runnable(&snap, file_id, runnable)?;
1081             if lens_config.run {
1082                 let lens = CodeLens {
1083                     range,
1084                     command: Some(run_single_command(&r, action.run_title)),
1085                     data: None,
1086                 };
1087                 lenses.push(lens);
1088             }
1089
1090             if action.debugee && lens_config.debug {
1091                 let debug_lens =
1092                     CodeLens { range, command: Some(debug_single_command(&r)), data: None };
1093                 lenses.push(debug_lens);
1094             }
1095         }
1096     }
1097
1098     if lens_config.implementations {
1099         // Handle impls
1100         lenses.extend(
1101             snap.analysis
1102                 .file_structure(file_id)?
1103                 .into_iter()
1104                 .filter(|it| {
1105                     matches!(
1106                         it.kind,
1107                         SymbolKind::Trait
1108                             | SymbolKind::Struct
1109                             | SymbolKind::Enum
1110                             | SymbolKind::Union
1111                     )
1112                 })
1113                 .map(|it| {
1114                     let range = to_proto::range(&line_index, it.node_range);
1115                     let pos = range.start;
1116                     let lens_params = lsp_types::request::GotoImplementationParams {
1117                         text_document_position_params: lsp_types::TextDocumentPositionParams::new(
1118                             params.text_document.clone(),
1119                             pos,
1120                         ),
1121                         work_done_progress_params: Default::default(),
1122                         partial_result_params: Default::default(),
1123                     };
1124                     CodeLens {
1125                         range,
1126                         command: None,
1127                         data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()),
1128                     }
1129                 }),
1130         );
1131     }
1132
1133     if lens_config.references() {
1134         lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| {
1135             let range = to_proto::range(&line_index, it.range);
1136             let position = to_proto::position(&line_index, it.range.start());
1137             let lens_params =
1138                 lsp_types::TextDocumentPositionParams::new(params.text_document.clone(), position);
1139
1140             CodeLens {
1141                 range,
1142                 command: None,
1143                 data: Some(to_value(CodeLensResolveData::References(lens_params)).unwrap()),
1144             }
1145         }));
1146     }
1147
1148     Ok(Some(lenses))
1149 }
1150
1151 #[derive(Debug, Serialize, Deserialize)]
1152 #[serde(rename_all = "camelCase")]
1153 enum CodeLensResolveData {
1154     Impls(lsp_types::request::GotoImplementationParams),
1155     References(lsp_types::TextDocumentPositionParams),
1156 }
1157
1158 pub(crate) fn handle_code_lens_resolve(
1159     snap: GlobalStateSnapshot,
1160     code_lens: CodeLens,
1161 ) -> Result<CodeLens> {
1162     let _p = profile::span("handle_code_lens_resolve");
1163     let data = code_lens.data.unwrap();
1164     let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
1165     match resolve {
1166         Some(CodeLensResolveData::Impls(lens_params)) => {
1167             let locations: Vec<Location> =
1168                 match handle_goto_implementation(snap, lens_params.clone())? {
1169                     Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
1170                     Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
1171                     Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
1172                         .into_iter()
1173                         .map(|link| Location::new(link.target_uri, link.target_selection_range))
1174                         .collect(),
1175                     _ => vec![],
1176                 };
1177
1178             let title = implementation_title(locations.len());
1179             let cmd = show_references_command(
1180                 title,
1181                 &lens_params.text_document_position_params.text_document.uri,
1182                 code_lens.range.start,
1183                 locations,
1184             );
1185             Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
1186         }
1187         Some(CodeLensResolveData::References(doc_position)) => {
1188             let position = from_proto::file_position(&snap, doc_position.clone())?;
1189             let locations = snap
1190                 .analysis
1191                 .find_all_refs(position, None)
1192                 .unwrap_or(None)
1193                 .map(|r| {
1194                     r.references()
1195                         .file_ranges()
1196                         .filter_map(|frange| to_proto::location(&snap, frange).ok())
1197                         .collect_vec()
1198                 })
1199                 .unwrap_or_default();
1200
1201             let title = reference_title(locations.len());
1202             let cmd = if locations.is_empty() {
1203                 Command { title, command: "".into(), arguments: None }
1204             } else {
1205                 show_references_command(
1206                     title,
1207                     &doc_position.text_document.uri,
1208                     code_lens.range.start,
1209                     locations,
1210                 )
1211             };
1212
1213             Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
1214         }
1215         None => Ok(CodeLens {
1216             range: code_lens.range,
1217             command: Some(Command { title: "Error".into(), ..Default::default() }),
1218             data: None,
1219         }),
1220     }
1221 }
1222
1223 pub(crate) fn handle_document_highlight(
1224     snap: GlobalStateSnapshot,
1225     params: lsp_types::DocumentHighlightParams,
1226 ) -> Result<Option<Vec<DocumentHighlight>>> {
1227     let _p = profile::span("handle_document_highlight");
1228     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1229     let line_index = snap.analysis.file_line_index(position.file_id)?;
1230
1231     let refs = match snap
1232         .analysis
1233         .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
1234     {
1235         None => return Ok(None),
1236         Some(refs) => refs,
1237     };
1238
1239     let res = refs
1240         .references_with_declaration()
1241         .references
1242         .get(&position.file_id)
1243         .map(|file_refs| {
1244             file_refs
1245                 .into_iter()
1246                 .map(|r| DocumentHighlight {
1247                     range: to_proto::range(&line_index, r.range),
1248                     kind: r.access.map(to_proto::document_highlight_kind),
1249                 })
1250                 .collect()
1251         })
1252         .unwrap_or_default();
1253     Ok(Some(res))
1254 }
1255
1256 pub(crate) fn handle_ssr(
1257     snap: GlobalStateSnapshot,
1258     params: lsp_ext::SsrParams,
1259 ) -> Result<lsp_types::WorkspaceEdit> {
1260     let _p = profile::span("handle_ssr");
1261     let selections = params
1262         .selections
1263         .iter()
1264         .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
1265         .collect::<Result<Vec<_>, _>>()?;
1266     let position = from_proto::file_position(&snap, params.position)?;
1267     let source_change = snap.analysis.structural_search_replace(
1268         &params.query,
1269         params.parse_only,
1270         position,
1271         selections,
1272     )??;
1273     to_proto::workspace_edit(&snap, source_change)
1274 }
1275
1276 pub(crate) fn publish_diagnostics(
1277     snap: &GlobalStateSnapshot,
1278     file_id: FileId,
1279 ) -> Result<Vec<Diagnostic>> {
1280     let _p = profile::span("publish_diagnostics");
1281     let line_index = snap.analysis.file_line_index(file_id)?;
1282
1283     let diagnostics: Vec<Diagnostic> = snap
1284         .analysis
1285         .diagnostics(&snap.config.diagnostics(), file_id)?
1286         .into_iter()
1287         .map(|d| Diagnostic {
1288             range: to_proto::range(&line_index, d.range),
1289             severity: Some(to_proto::diagnostic_severity(d.severity)),
1290             code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String),
1291             code_description: d.code.and_then(|code| {
1292                 lsp_types::Url::parse(&format!(
1293                     "https://rust-analyzer.github.io/manual.html#{}",
1294                     code.as_str()
1295                 ))
1296                 .ok()
1297                 .map(|href| lsp_types::CodeDescription { href })
1298             }),
1299             source: Some("rust-analyzer".to_string()),
1300             message: d.message,
1301             related_information: None,
1302             tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
1303             data: None,
1304         })
1305         .collect();
1306     Ok(diagnostics)
1307 }
1308
1309 pub(crate) fn handle_inlay_hints(
1310     snap: GlobalStateSnapshot,
1311     params: InlayHintsParams,
1312 ) -> Result<Vec<InlayHint>> {
1313     let _p = profile::span("handle_inlay_hints");
1314     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1315     let line_index = snap.analysis.file_line_index(file_id)?;
1316     Ok(snap
1317         .analysis
1318         .inlay_hints(file_id, &snap.config.inlay_hints())?
1319         .into_iter()
1320         .map(|it| to_proto::inlay_hint(&line_index, it))
1321         .collect())
1322 }
1323
1324 pub(crate) fn handle_call_hierarchy_prepare(
1325     snap: GlobalStateSnapshot,
1326     params: CallHierarchyPrepareParams,
1327 ) -> Result<Option<Vec<CallHierarchyItem>>> {
1328     let _p = profile::span("handle_call_hierarchy_prepare");
1329     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1330
1331     let nav_info = match snap.analysis.call_hierarchy(position)? {
1332         None => return Ok(None),
1333         Some(it) => it,
1334     };
1335
1336     let RangeInfo { range: _, info: navs } = nav_info;
1337     let res = navs
1338         .into_iter()
1339         .filter(|it| it.kind == Some(SymbolKind::Function))
1340         .map(|it| to_proto::call_hierarchy_item(&snap, it))
1341         .collect::<Result<Vec<_>>>()?;
1342
1343     Ok(Some(res))
1344 }
1345
1346 pub(crate) fn handle_call_hierarchy_incoming(
1347     snap: GlobalStateSnapshot,
1348     params: CallHierarchyIncomingCallsParams,
1349 ) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1350     let _p = profile::span("handle_call_hierarchy_incoming");
1351     let item = params.item;
1352
1353     let doc = TextDocumentIdentifier::new(item.uri);
1354     let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1355     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1356
1357     let call_items = match snap.analysis.incoming_calls(fpos)? {
1358         None => return Ok(None),
1359         Some(it) => it,
1360     };
1361
1362     let mut res = vec![];
1363
1364     for call_item in call_items.into_iter() {
1365         let file_id = call_item.target.file_id;
1366         let line_index = snap.analysis.file_line_index(file_id)?;
1367         let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1368         res.push(CallHierarchyIncomingCall {
1369             from: item,
1370             from_ranges: call_item
1371                 .ranges
1372                 .into_iter()
1373                 .map(|it| to_proto::range(&line_index, it))
1374                 .collect(),
1375         });
1376     }
1377
1378     Ok(Some(res))
1379 }
1380
1381 pub(crate) fn handle_call_hierarchy_outgoing(
1382     snap: GlobalStateSnapshot,
1383     params: CallHierarchyOutgoingCallsParams,
1384 ) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1385     let _p = profile::span("handle_call_hierarchy_outgoing");
1386     let item = params.item;
1387
1388     let doc = TextDocumentIdentifier::new(item.uri);
1389     let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1390     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1391
1392     let call_items = match snap.analysis.outgoing_calls(fpos)? {
1393         None => return Ok(None),
1394         Some(it) => it,
1395     };
1396
1397     let mut res = vec![];
1398
1399     for call_item in call_items.into_iter() {
1400         let file_id = call_item.target.file_id;
1401         let line_index = snap.analysis.file_line_index(file_id)?;
1402         let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1403         res.push(CallHierarchyOutgoingCall {
1404             to: item,
1405             from_ranges: call_item
1406                 .ranges
1407                 .into_iter()
1408                 .map(|it| to_proto::range(&line_index, it))
1409                 .collect(),
1410         });
1411     }
1412
1413     Ok(Some(res))
1414 }
1415
1416 pub(crate) fn handle_semantic_tokens_full(
1417     snap: GlobalStateSnapshot,
1418     params: SemanticTokensParams,
1419 ) -> Result<Option<SemanticTokensResult>> {
1420     let _p = profile::span("handle_semantic_tokens_full");
1421
1422     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1423     let text = snap.analysis.file_text(file_id)?;
1424     let line_index = snap.analysis.file_line_index(file_id)?;
1425
1426     let highlights = snap.analysis.highlight(file_id)?;
1427     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1428
1429     // Unconditionally cache the tokens
1430     snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
1431
1432     Ok(Some(semantic_tokens.into()))
1433 }
1434
1435 pub(crate) fn handle_semantic_tokens_full_delta(
1436     snap: GlobalStateSnapshot,
1437     params: SemanticTokensDeltaParams,
1438 ) -> Result<Option<SemanticTokensFullDeltaResult>> {
1439     let _p = profile::span("handle_semantic_tokens_full_delta");
1440
1441     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1442     let text = snap.analysis.file_text(file_id)?;
1443     let line_index = snap.analysis.file_line_index(file_id)?;
1444
1445     let highlights = snap.analysis.highlight(file_id)?;
1446
1447     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1448
1449     let mut cache = snap.semantic_tokens_cache.lock();
1450     let cached_tokens = cache.entry(params.text_document.uri).or_default();
1451
1452     if let Some(prev_id) = &cached_tokens.result_id {
1453         if *prev_id == params.previous_result_id {
1454             let delta = to_proto::semantic_token_delta(&cached_tokens, &semantic_tokens);
1455             *cached_tokens = semantic_tokens;
1456             return Ok(Some(delta.into()));
1457         }
1458     }
1459
1460     *cached_tokens = semantic_tokens.clone();
1461
1462     Ok(Some(semantic_tokens.into()))
1463 }
1464
1465 pub(crate) fn handle_semantic_tokens_range(
1466     snap: GlobalStateSnapshot,
1467     params: SemanticTokensRangeParams,
1468 ) -> Result<Option<SemanticTokensRangeResult>> {
1469     let _p = profile::span("handle_semantic_tokens_range");
1470
1471     let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1472     let text = snap.analysis.file_text(frange.file_id)?;
1473     let line_index = snap.analysis.file_line_index(frange.file_id)?;
1474
1475     let highlights = snap.analysis.highlight_range(frange)?;
1476     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1477     Ok(Some(semantic_tokens.into()))
1478 }
1479
1480 pub(crate) fn handle_open_docs(
1481     snap: GlobalStateSnapshot,
1482     params: lsp_types::TextDocumentPositionParams,
1483 ) -> Result<Option<lsp_types::Url>> {
1484     let _p = profile::span("handle_open_docs");
1485     let position = from_proto::file_position(&snap, params)?;
1486
1487     let remote = snap.analysis.external_docs(position)?;
1488
1489     Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
1490 }
1491
1492 pub(crate) fn handle_open_cargo_toml(
1493     snap: GlobalStateSnapshot,
1494     params: lsp_ext::OpenCargoTomlParams,
1495 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
1496     let _p = profile::span("handle_open_cargo_toml");
1497     let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1498
1499     let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
1500         Some(it) => it,
1501         None => return Ok(None),
1502     };
1503
1504     let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
1505     let res: lsp_types::GotoDefinitionResponse =
1506         Location::new(cargo_toml_url, Range::default()).into();
1507     Ok(Some(res))
1508 }
1509
1510 fn implementation_title(count: usize) -> String {
1511     if count == 1 {
1512         "1 implementation".into()
1513     } else {
1514         format!("{} implementations", count)
1515     }
1516 }
1517
1518 fn reference_title(count: usize) -> String {
1519     if count == 1 {
1520         "1 reference".into()
1521     } else {
1522         format!("{} references", count)
1523     }
1524 }
1525
1526 fn show_references_command(
1527     title: String,
1528     uri: &lsp_types::Url,
1529     position: lsp_types::Position,
1530     locations: Vec<lsp_types::Location>,
1531 ) -> Command {
1532     // We cannot use the 'editor.action.showReferences' command directly
1533     // because that command requires vscode types which we convert in the handler
1534     // on the client side.
1535
1536     Command {
1537         title,
1538         command: "rust-analyzer.showReferences".into(),
1539         arguments: Some(vec![
1540             to_value(uri).unwrap(),
1541             to_value(position).unwrap(),
1542             to_value(locations).unwrap(),
1543         ]),
1544     }
1545 }
1546
1547 fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1548     Command {
1549         title: title.to_string(),
1550         command: "rust-analyzer.runSingle".into(),
1551         arguments: Some(vec![to_value(runnable).unwrap()]),
1552     }
1553 }
1554
1555 fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1556     Command {
1557         title: "Debug".into(),
1558         command: "rust-analyzer.debugSingle".into(),
1559         arguments: Some(vec![to_value(runnable).unwrap()]),
1560     }
1561 }
1562
1563 fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1564     let value = if snap.config.location_link() {
1565         let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1566         to_value(link).ok()?
1567     } else {
1568         let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
1569         let location = to_proto::location(snap, range).ok()?;
1570         to_value(location).ok()?
1571     };
1572
1573     Some(Command {
1574         title: nav.name.to_string(),
1575         command: "rust-analyzer.gotoLocation".into(),
1576         arguments: Some(vec![value]),
1577     })
1578 }
1579
1580 fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1581     lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1582 }
1583
1584 fn show_impl_command_link(
1585     snap: &GlobalStateSnapshot,
1586     position: &FilePosition,
1587 ) -> Option<lsp_ext::CommandLinkGroup> {
1588     if snap.config.hover().implementations {
1589         if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
1590             let uri = to_proto::url(snap, position.file_id);
1591             let line_index = snap.analysis.file_line_index(position.file_id).ok()?;
1592             let position = to_proto::position(&line_index, position.offset);
1593             let locations: Vec<_> = nav_data
1594                 .info
1595                 .into_iter()
1596                 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1597                 .collect();
1598             let title = implementation_title(locations.len());
1599             let command = show_references_command(title, &uri, position, locations);
1600
1601             return Some(lsp_ext::CommandLinkGroup {
1602                 commands: vec![to_command_link(command, "Go to implementations".into())],
1603                 ..Default::default()
1604             });
1605         }
1606     }
1607     None
1608 }
1609
1610 fn runnable_action_links(
1611     snap: &GlobalStateSnapshot,
1612     file_id: FileId,
1613     runnable: Runnable,
1614 ) -> Option<lsp_ext::CommandLinkGroup> {
1615     let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1616     let hover_config = snap.config.hover();
1617     if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1618         return None;
1619     }
1620
1621     let action: &'static _ = runnable.action();
1622     to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1623         let mut group = lsp_ext::CommandLinkGroup::default();
1624
1625         if hover_config.run {
1626             let run_command = run_single_command(&r, action.run_title);
1627             group.commands.push(to_command_link(run_command, r.label.clone()));
1628         }
1629
1630         if hover_config.debug {
1631             let dbg_command = debug_single_command(&r);
1632             group.commands.push(to_command_link(dbg_command, r.label));
1633         }
1634
1635         group
1636     })
1637 }
1638
1639 fn goto_type_action_links(
1640     snap: &GlobalStateSnapshot,
1641     nav_targets: &[HoverGotoTypeData],
1642 ) -> Option<lsp_ext::CommandLinkGroup> {
1643     if !snap.config.hover().goto_type_def || nav_targets.is_empty() {
1644         return None;
1645     }
1646
1647     Some(lsp_ext::CommandLinkGroup {
1648         title: Some("Go to ".into()),
1649         commands: nav_targets
1650             .iter()
1651             .filter_map(|it| {
1652                 goto_location_command(snap, &it.nav)
1653                     .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
1654             })
1655             .collect(),
1656     })
1657 }
1658
1659 fn prepare_hover_actions(
1660     snap: &GlobalStateSnapshot,
1661     file_id: FileId,
1662     actions: &[HoverAction],
1663 ) -> Vec<lsp_ext::CommandLinkGroup> {
1664     if snap.config.hover().none() || !snap.config.hover_actions() {
1665         return Vec::new();
1666     }
1667
1668     actions
1669         .iter()
1670         .filter_map(|it| match it {
1671             HoverAction::Implementation(position) => show_impl_command_link(snap, position),
1672             HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
1673             HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1674         })
1675         .collect()
1676 }
1677
1678 fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
1679     match runnable.kind {
1680         RunnableKind::Bin => {
1681             // Do not suggest binary run on other target than binary
1682             match &cargo_spec {
1683                 Some(spec) => !matches!(
1684                     spec.target_kind,
1685                     TargetKind::Bin | TargetKind::Example | TargetKind::Test
1686                 ),
1687                 None => true,
1688             }
1689         }
1690         _ => false,
1691     }
1692 }
1693
1694 #[derive(Debug, Serialize, Deserialize)]
1695 struct CompletionResolveData {
1696     position: lsp_types::TextDocumentPositionParams,
1697     full_import_path: String,
1698     imported_name: String,
1699     import_for_trait_assoc_item: bool,
1700 }
1701
1702 fn fill_resolve_data(
1703     resolve_data: &mut Option<serde_json::Value>,
1704     item: &ide::CompletionItem,
1705     position: &TextDocumentPositionParams,
1706 ) -> Option<()> {
1707     let import_edit = item.import_to_add()?;
1708     let full_import_path = import_edit.import_path.to_string();
1709     let imported_name = import_edit.import_path.segments.clone().pop()?.to_string();
1710
1711     *resolve_data = Some(
1712         to_value(CompletionResolveData {
1713             position: position.to_owned(),
1714             full_import_path,
1715             imported_name,
1716             import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1717         })
1718         .unwrap(),
1719     );
1720     Some(())
1721 }