1 //! This module is responsible for implementing handlers for Language Server
2 //! Protocol. The majority of requests are fulfilled by calling into the
7 process::{self, Stdio},
11 AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, Query,
12 RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit,
14 use ide_db::SymbolKind;
15 use itertools::Itertools;
16 use lsp_server::ErrorCode;
18 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
19 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
20 CodeActionKind, CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams,
21 DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString,
22 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
23 SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
24 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
25 TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
27 use project_model::TargetKind;
28 use serde::{Deserialize, Serialize};
29 use serde_json::to_value;
30 use stdx::{format_to, split_once};
31 use syntax::{algo, ast, AstNode, TextRange, TextSize};
34 cargo_target_spec::CargoTargetSpec,
35 config::RustfmtConfig,
38 global_state::{GlobalState, GlobalStateSnapshot},
39 line_index::{LineEndings, LineIndex},
40 lsp_ext::{self, InlayHint, InlayHintsParams},
41 lsp_utils::all_edits_are_disjoint,
42 to_proto, LspError, Result,
45 pub(crate) fn handle_analyzer_status(
46 snap: GlobalStateSnapshot,
47 params: lsp_ext::AnalyzerStatusParams,
49 let _p = profile::span("handle_analyzer_status");
51 let mut buf = String::new();
53 let mut file_id = None;
54 if let Some(tdi) = params.text_document {
55 match from_proto::file_id(&snap, &tdi.uri) {
56 Ok(it) => file_id = Some(it),
57 Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri),
61 if snap.workspaces.is_empty() {
62 buf.push_str("no workspaces\n")
64 buf.push_str("workspaces:\n");
65 for w in snap.workspaces.iter() {
66 format_to!(buf, "{} packages loaded\n", w.n_packages());
69 buf.push_str("\nanalysis:\n");
74 .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
76 format_to!(buf, "\n\nrequests:\n");
77 let requests = snap.latest_requests.read();
78 for (is_last, r) in requests.iter() {
79 let mark = if is_last { "*" } else { " " };
80 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis());
85 pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
86 let _p = profile::span("handle_memory_usage");
87 let mem = state.analysis_host.per_query_memory_usage();
89 let mut out = String::new();
90 for (name, bytes) in mem {
91 format_to!(out, "{:>8} {}\n", bytes, name);
96 pub(crate) fn handle_syntax_tree(
97 snap: GlobalStateSnapshot,
98 params: lsp_ext::SyntaxTreeParams,
100 let _p = profile::span("handle_syntax_tree");
101 let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
102 let line_index = snap.file_line_index(id)?;
103 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
104 let res = snap.analysis.syntax_tree(id, text_range)?;
108 pub(crate) fn handle_view_hir(
109 snap: GlobalStateSnapshot,
110 params: lsp_types::TextDocumentPositionParams,
111 ) -> Result<String> {
112 let _p = profile::span("handle_view_hir");
113 let position = from_proto::file_position(&snap, params)?;
114 let res = snap.analysis.view_hir(position)?;
118 pub(crate) fn handle_expand_macro(
119 snap: GlobalStateSnapshot,
120 params: lsp_ext::ExpandMacroParams,
121 ) -> Result<Option<lsp_ext::ExpandedMacro>> {
122 let _p = profile::span("handle_expand_macro");
123 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
124 let line_index = snap.file_line_index(file_id)?;
125 let offset = from_proto::offset(&line_index, params.position);
127 let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?;
128 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
131 pub(crate) fn handle_selection_range(
132 snap: GlobalStateSnapshot,
133 params: lsp_types::SelectionRangeParams,
134 ) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
135 let _p = profile::span("handle_selection_range");
136 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
137 let line_index = snap.file_line_index(file_id)?;
138 let res: Result<Vec<lsp_types::SelectionRange>> = params
142 let offset = from_proto::offset(&line_index, position);
143 let mut ranges = Vec::new();
145 let mut range = TextRange::new(offset, offset);
148 let frange = FileRange { file_id, range };
149 let next = snap.analysis.extend_selection(frange)?;
157 let mut range = lsp_types::SelectionRange {
158 range: to_proto::range(&line_index, *ranges.last().unwrap()),
161 for &r in ranges.iter().rev().skip(1) {
162 range = lsp_types::SelectionRange {
163 range: to_proto::range(&line_index, r),
164 parent: Some(Box::new(range)),
174 pub(crate) fn handle_matching_brace(
175 snap: GlobalStateSnapshot,
176 params: lsp_ext::MatchingBraceParams,
177 ) -> Result<Vec<Position>> {
178 let _p = profile::span("handle_matching_brace");
179 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
180 let line_index = snap.file_line_index(file_id)?;
185 let offset = from_proto::offset(&line_index, position);
186 let offset = match snap.analysis.matching_brace(FilePosition { file_id, offset }) {
187 Ok(Some(matching_brace_offset)) => matching_brace_offset,
188 Err(_) | Ok(None) => offset,
190 to_proto::position(&line_index, offset)
196 pub(crate) fn handle_join_lines(
197 snap: GlobalStateSnapshot,
198 params: lsp_ext::JoinLinesParams,
199 ) -> Result<Vec<lsp_types::TextEdit>> {
200 let _p = profile::span("handle_join_lines");
201 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
202 let line_index = snap.file_line_index(file_id)?;
203 let mut res = TextEdit::default();
204 for range in params.ranges {
205 let range = from_proto::text_range(&line_index, range);
206 let edit = snap.analysis.join_lines(FileRange { file_id, range })?;
207 match res.union(edit) {
210 // just ignore overlapping edits
214 let res = to_proto::text_edit_vec(&line_index, res);
218 pub(crate) fn handle_on_enter(
219 snap: GlobalStateSnapshot,
220 params: lsp_types::TextDocumentPositionParams,
221 ) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
222 let _p = profile::span("handle_on_enter");
223 let position = from_proto::file_position(&snap, params)?;
224 let edit = match snap.analysis.on_enter(position)? {
225 None => return Ok(None),
228 let line_index = snap.file_line_index(position.file_id)?;
229 let edit = to_proto::snippet_text_edit_vec(&line_index, true, edit);
233 // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
234 pub(crate) fn handle_on_type_formatting(
235 snap: GlobalStateSnapshot,
236 params: lsp_types::DocumentOnTypeFormattingParams,
237 ) -> Result<Option<Vec<lsp_types::TextEdit>>> {
238 let _p = profile::span("handle_on_type_formatting");
239 let mut position = from_proto::file_position(&snap, params.text_document_position)?;
240 let line_index = snap.file_line_index(position.file_id)?;
242 // in `ide`, the `on_type` invariant is that
243 // `text.char_at(position) == typed_char`.
244 position.offset -= TextSize::of('.');
245 let char_typed = params.ch.chars().next().unwrap_or('\0');
247 let text = snap.analysis.file_text(position.file_id)?;
248 text[usize::from(position.offset)..].starts_with(char_typed)
251 // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
252 // but it requires precise cursor positioning to work, and one can't
253 // position the cursor with on_type formatting. So, let's just toggle this
254 // feature off here, hoping that we'll enable it one day, 😿.
255 if char_typed == '>' {
259 let edit = snap.analysis.on_char_typed(position, char_typed)?;
260 let edit = match edit {
262 None => return Ok(None),
265 // This should be a single-file edit
266 let (_, edit) = edit.source_file_edits.into_iter().next().unwrap();
268 let change = to_proto::text_edit_vec(&line_index, edit);
272 pub(crate) fn handle_document_symbol(
273 snap: GlobalStateSnapshot,
274 params: lsp_types::DocumentSymbolParams,
275 ) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
276 let _p = profile::span("handle_document_symbol");
277 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
278 let line_index = snap.file_line_index(file_id)?;
280 let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new();
282 for symbol in snap.analysis.file_structure(file_id)? {
283 let mut tags = Vec::new();
284 if symbol.deprecated {
285 tags.push(SymbolTag::Deprecated)
289 let doc_symbol = lsp_types::DocumentSymbol {
291 detail: symbol.detail,
292 kind: to_proto::symbol_kind(symbol.kind),
294 deprecated: Some(symbol.deprecated),
295 range: to_proto::range(&line_index, symbol.node_range),
296 selection_range: to_proto::range(&line_index, symbol.navigation_range),
299 parents.push((doc_symbol, symbol.parent));
302 // Builds hierarchy from a flat list, in reverse order (so that indices
304 let document_symbols = {
305 let mut acc = Vec::new();
306 while let Some((mut node, parent_idx)) = parents.pop() {
307 if let Some(children) = &mut node.children {
310 let parent = match parent_idx {
312 Some(i) => parents[i].0.children.get_or_insert_with(Vec::new),
320 let res = if snap.config.hierarchical_symbols() {
321 document_symbols.into()
323 let url = to_proto::url(&snap, file_id);
324 let mut symbol_information = Vec::<SymbolInformation>::new();
325 for symbol in document_symbols {
326 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
328 symbol_information.into()
330 return Ok(Some(res));
332 fn flatten_document_symbol(
333 symbol: &lsp_types::DocumentSymbol,
334 container_name: Option<String>,
336 res: &mut Vec<SymbolInformation>,
338 let mut tags = Vec::new();
341 match symbol.deprecated {
342 Some(true) => tags.push(SymbolTag::Deprecated),
347 res.push(SymbolInformation {
348 name: symbol.name.clone(),
351 deprecated: symbol.deprecated,
352 location: Location::new(url.clone(), symbol.range),
356 for child in symbol.children.iter().flatten() {
357 flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
362 pub(crate) fn handle_workspace_symbol(
363 snap: GlobalStateSnapshot,
364 params: lsp_types::WorkspaceSymbolParams,
365 ) -> Result<Option<Vec<SymbolInformation>>> {
366 let _p = profile::span("handle_workspace_symbol");
367 let all_symbols = params.query.contains('#');
368 let libs = params.query.contains('*');
370 let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect();
371 let mut q = Query::new(query);
381 let mut res = exec_query(&snap, query)?;
382 if res.is_empty() && !all_symbols {
383 let mut query = Query::new(params.query);
385 res = exec_query(&snap, query)?;
388 return Ok(Some(res));
390 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
391 let mut res = Vec::new();
392 for nav in snap.analysis.symbol_search(query)? {
393 let container_name = nav.container_name.as_ref().map(|v| v.to_string());
396 let info = SymbolInformation {
397 name: nav.name.to_string(),
400 .map(to_proto::symbol_kind)
401 .unwrap_or(lsp_types::SymbolKind::Variable),
403 location: to_proto::location_from_nav(snap, nav)?,
413 pub(crate) fn handle_will_rename_files(
414 snap: GlobalStateSnapshot,
415 params: lsp_types::RenameFilesParams,
416 ) -> Result<Option<lsp_types::WorkspaceEdit>> {
417 let _p = profile::span("handle_will_rename_files");
419 let source_changes: Vec<SourceChange> = params
422 .filter_map(|file_rename| {
423 let from = Url::parse(&file_rename.old_uri).ok()?;
424 let to = Url::parse(&file_rename.new_uri).ok()?;
426 let from_path = from.to_file_path().ok()?;
427 let to_path = to.to_file_path().ok()?;
429 // Limit to single-level moves for now.
430 match (from_path.parent(), to_path.parent()) {
431 (Some(p1), Some(p2)) if p1 == p2 => {
432 if from_path.is_dir() {
433 // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
434 let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
435 old_folder_name.push('/');
436 let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
438 let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
439 let new_file_name = to_path.file_name()?.to_str()?;
441 snap.url_to_file_id(&imitate_from_url).ok()?,
442 new_file_name.to_string(),
445 let old_name = from_path.file_stem()?.to_str()?;
446 let new_name = to_path.file_stem()?.to_str()?;
447 match (old_name, new_name) {
450 _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
457 .filter_map(|(file_id, new_name)| {
458 snap.analysis.will_rename_file(file_id, &new_name).ok()?
462 // Drop file system edits since we're just renaming things on the same level
463 let mut source_changes = source_changes.into_iter();
464 let mut source_change = source_changes.next().unwrap_or_default();
465 source_change.file_system_edits.clear();
466 // no collect here because we want to merge text edits on same file ids
467 source_change.extend(source_changes.map(|it| it.source_file_edits).flatten());
468 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
469 Ok(Some(workspace_edit))
472 pub(crate) fn handle_goto_definition(
473 snap: GlobalStateSnapshot,
474 params: lsp_types::GotoDefinitionParams,
475 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
476 let _p = profile::span("handle_goto_definition");
477 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
478 let nav_info = match snap.analysis.goto_definition(position)? {
479 None => return Ok(None),
482 let src = FileRange { file_id: position.file_id, range: nav_info.range };
483 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
487 pub(crate) fn handle_goto_implementation(
488 snap: GlobalStateSnapshot,
489 params: lsp_types::request::GotoImplementationParams,
490 ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
491 let _p = profile::span("handle_goto_implementation");
492 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
493 let nav_info = match snap.analysis.goto_implementation(position)? {
494 None => return Ok(None),
497 let src = FileRange { file_id: position.file_id, range: nav_info.range };
498 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
502 pub(crate) fn handle_goto_type_definition(
503 snap: GlobalStateSnapshot,
504 params: lsp_types::request::GotoTypeDefinitionParams,
505 ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
506 let _p = profile::span("handle_goto_type_definition");
507 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
508 let nav_info = match snap.analysis.goto_type_definition(position)? {
509 None => return Ok(None),
512 let src = FileRange { file_id: position.file_id, range: nav_info.range };
513 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
517 pub(crate) fn handle_parent_module(
518 snap: GlobalStateSnapshot,
519 params: lsp_types::TextDocumentPositionParams,
520 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
521 let _p = profile::span("handle_parent_module");
522 let position = from_proto::file_position(&snap, params)?;
523 let navs = snap.analysis.parent_module(position)?;
524 let res = to_proto::goto_definition_response(&snap, None, navs)?;
528 pub(crate) fn handle_runnables(
529 snap: GlobalStateSnapshot,
530 params: lsp_ext::RunnablesParams,
531 ) -> Result<Vec<lsp_ext::Runnable>> {
532 let _p = profile::span("handle_runnables");
533 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
534 let line_index = snap.file_line_index(file_id)?;
535 let offset = params.position.map(|it| from_proto::offset(&line_index, it));
536 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
538 let expect_test = match offset {
540 let source_file = snap.analysis.parse(file_id)?;
541 algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
542 .and_then(|it| it.path()?.segment()?.name_ref())
543 .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
548 let mut res = Vec::new();
549 for runnable in snap.analysis.runnables(file_id)? {
550 if let Some(offset) = offset {
551 if !runnable.nav.full_range.contains_inclusive(offset) {
555 if should_skip_target(&runnable, cargo_spec.as_ref()) {
558 let mut runnable = to_proto::runnable(&snap, file_id, runnable)?;
560 runnable.label = format!("{} + expect", runnable.label);
561 runnable.args.expect_test = Some(true);
566 // Add `cargo check` and `cargo test` for all targets of the whole package
567 let config = snap.config.runnables();
570 for &cmd in ["check", "test"].iter() {
571 res.push(lsp_ext::Runnable {
572 label: format!("cargo {} -p {} --all-targets", cmd, spec.package),
574 kind: lsp_ext::RunnableKind::Cargo,
575 args: lsp_ext::CargoRunnable {
576 workspace_root: Some(spec.workspace_root.clone().into()),
577 override_cargo: config.override_cargo.clone(),
580 "--package".to_string(),
581 spec.package.clone(),
582 "--all-targets".to_string(),
584 cargo_extra_args: config.cargo_extra_args.clone(),
585 executable_args: Vec::new(),
592 res.push(lsp_ext::Runnable {
593 label: "cargo check --workspace".to_string(),
595 kind: lsp_ext::RunnableKind::Cargo,
596 args: lsp_ext::CargoRunnable {
597 workspace_root: None,
598 override_cargo: config.override_cargo,
599 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
600 cargo_extra_args: config.cargo_extra_args,
601 executable_args: Vec::new(),
610 pub(crate) fn handle_completion(
611 snap: GlobalStateSnapshot,
612 params: lsp_types::CompletionParams,
613 ) -> Result<Option<lsp_types::CompletionResponse>> {
614 let _p = profile::span("handle_completion");
615 let text_document_position = params.text_document_position.clone();
616 let position = from_proto::file_position(&snap, params.text_document_position)?;
617 let completion_triggered_after_single_colon = {
619 if let Some(ctx) = params.context {
620 if ctx.trigger_character.unwrap_or_default() == ":" {
621 let source_file = snap.analysis.parse(position.file_id)?;
622 let syntax = source_file.syntax();
623 let text = syntax.text();
624 if let Some(next_char) = text.char_at(position.offset) {
625 let diff = TextSize::of(next_char) + TextSize::of(':');
626 let prev_char = position.offset - diff;
627 if text.char_at(prev_char) != Some(':') {
635 if completion_triggered_after_single_colon {
639 let completion_config = &snap.config.completion();
640 let items = match snap.analysis.completions(completion_config, position)? {
641 None => return Ok(None),
642 Some(items) => items,
644 let line_index = snap.file_line_index(position.file_id)?;
646 let items: Vec<CompletionItem> = items
649 let mut new_completion_items = to_proto::completion_item(&line_index, item.clone());
651 if completion_config.enable_imports_on_the_fly {
652 for new_item in &mut new_completion_items {
653 fill_resolve_data(&mut new_item.data, &item, &text_document_position);
661 let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
662 Ok(Some(completion_list.into()))
665 pub(crate) fn handle_completion_resolve(
666 snap: GlobalStateSnapshot,
667 mut original_completion: CompletionItem,
668 ) -> Result<CompletionItem> {
669 let _p = profile::span("handle_completion_resolve");
671 if !all_edits_are_disjoint(&original_completion, &[]) {
672 return Err(LspError::new(
673 ErrorCode::InvalidParams as i32,
674 "Received a completion with overlapping edits, this is not LSP-compliant".into(),
679 let resolve_data = match original_completion
682 .map(|data| serde_json::from_value::<CompletionResolveData>(data))
686 None => return Ok(original_completion),
689 let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
690 let line_index = snap.file_line_index(file_id)?;
691 let offset = from_proto::offset(&line_index, resolve_data.position.position);
693 let additional_edits = snap
695 .resolve_completion_edits(
696 &snap.config.completion(),
697 FilePosition { file_id, offset },
698 &resolve_data.full_import_path,
699 resolve_data.imported_name,
700 resolve_data.import_for_trait_assoc_item,
703 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
706 if !all_edits_are_disjoint(&original_completion, &additional_edits) {
707 return Err(LspError::new(
708 ErrorCode::InternalError as i32,
709 "Import edit overlaps with the original completion edits, this is not LSP-compliant"
715 if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
716 original_additional_edits.extend(additional_edits.into_iter())
718 original_completion.additional_text_edits = Some(additional_edits);
721 Ok(original_completion)
724 pub(crate) fn handle_folding_range(
725 snap: GlobalStateSnapshot,
726 params: FoldingRangeParams,
727 ) -> Result<Option<Vec<FoldingRange>>> {
728 let _p = profile::span("handle_folding_range");
729 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
730 let folds = snap.analysis.folding_ranges(file_id)?;
731 let text = snap.analysis.file_text(file_id)?;
732 let line_index = snap.file_line_index(file_id)?;
733 let line_folding_only = snap.config.line_folding_only();
736 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
741 pub(crate) fn handle_signature_help(
742 snap: GlobalStateSnapshot,
743 params: lsp_types::SignatureHelpParams,
744 ) -> Result<Option<lsp_types::SignatureHelp>> {
745 let _p = profile::span("handle_signature_help");
746 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
747 let call_info = match snap.analysis.call_info(position)? {
749 None => return Ok(None),
751 let concise = !snap.config.call_info_full();
753 to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
757 pub(crate) fn handle_hover(
758 snap: GlobalStateSnapshot,
759 params: lsp_types::HoverParams,
760 ) -> Result<Option<lsp_ext::Hover>> {
761 let _p = profile::span("handle_hover");
762 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
763 let hover_config = snap.config.hover();
765 match snap.analysis.hover(position, hover_config.links_in_hover, hover_config.markdown)? {
766 None => return Ok(None),
769 let line_index = snap.file_line_index(position.file_id)?;
770 let range = to_proto::range(&line_index, info.range);
771 let hover = lsp_ext::Hover {
772 hover: lsp_types::Hover {
773 contents: HoverContents::Markup(to_proto::markup_content(info.info.markup)),
776 actions: prepare_hover_actions(&snap, position.file_id, &info.info.actions),
782 pub(crate) fn handle_prepare_rename(
783 snap: GlobalStateSnapshot,
784 params: lsp_types::TextDocumentPositionParams,
785 ) -> Result<Option<PrepareRenameResponse>> {
786 let _p = profile::span("handle_prepare_rename");
787 let position = from_proto::file_position(&snap, params)?;
789 let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
791 let line_index = snap.file_line_index(position.file_id)?;
792 let range = to_proto::range(&line_index, change.range);
793 Ok(Some(PrepareRenameResponse::Range(range)))
796 pub(crate) fn handle_rename(
797 snap: GlobalStateSnapshot,
798 params: RenameParams,
799 ) -> Result<Option<WorkspaceEdit>> {
800 let _p = profile::span("handle_rename");
801 let position = from_proto::file_position(&snap, params.text_document_position)?;
804 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
805 let workspace_edit = to_proto::workspace_edit(&snap, change)?;
806 Ok(Some(workspace_edit))
809 pub(crate) fn handle_references(
810 snap: GlobalStateSnapshot,
811 params: lsp_types::ReferenceParams,
812 ) -> Result<Option<Vec<Location>>> {
813 let _p = profile::span("handle_references");
814 let position = from_proto::file_position(&snap, params.text_document_position)?;
816 let refs = match snap.analysis.find_all_refs(position, None)? {
817 None => return Ok(None),
821 let decl = if params.context.include_declaration {
823 file_id: refs.declaration.nav.file_id,
824 range: refs.declaration.nav.focus_or_full_range(),
832 .flat_map(|(file_id, refs)| {
833 refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
836 .filter_map(|frange| to_proto::location(&snap, frange).ok())
842 pub(crate) fn handle_formatting(
843 snap: GlobalStateSnapshot,
844 params: DocumentFormattingParams,
845 ) -> Result<Option<Vec<lsp_types::TextEdit>>> {
846 let _p = profile::span("handle_formatting");
847 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
848 let file = snap.analysis.file_text(file_id)?;
849 let crate_ids = snap.analysis.crate_for(file_id)?;
851 let line_index = snap.file_line_index(file_id)?;
853 let mut rustfmt = match snap.config.rustfmt() {
854 RustfmtConfig::Rustfmt { extra_args } => {
855 let mut cmd = process::Command::new(toolchain::rustfmt());
856 cmd.args(extra_args);
857 // try to chdir to the file so we can respect `rustfmt.toml`
858 // FIXME: use `rustfmt --config-path` once
859 // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
860 match params.text_document.uri.to_file_path() {
863 if path.pop() && path.is_dir() {
864 cmd.current_dir(path);
869 "Unable to get file path for {}, rustfmt.toml might be ignored",
870 params.text_document.uri
874 if let Some(&crate_id) = crate_ids.first() {
875 // Assume all crates are in the same edition
876 let edition = snap.analysis.crate_edition(crate_id)?;
877 cmd.arg("--edition");
878 cmd.arg(edition.to_string());
882 RustfmtConfig::CustomCommand { command, args } => {
883 let mut cmd = process::Command::new(command);
890 rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
892 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
894 let output = rustfmt.wait_with_output()?;
895 let captured_stdout = String::from_utf8(output.stdout)?;
896 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
898 if !output.status.success() {
899 match output.status.code() {
900 Some(1) if !captured_stderr.contains("not installed") => {
901 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
902 // likely cause exiting with 1. Most Language Servers swallow parse errors on
903 // formatting because otherwise an error is surfaced to the user on top of the
904 // syntax error diagnostics they're already receiving. This is especially jarring
905 // if they have format on save enabled.
906 log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
910 // Something else happened - e.g. `rustfmt` is missing or caught a signal
911 return Err(LspError::new(
914 r#"rustfmt exited with:
918 output.status, captured_stdout, captured_stderr,
926 let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
928 if line_index.endings != new_line_endings {
929 // If line endings are different, send the entire file.
930 // Diffing would not work here, as the line endings might be the only
932 Ok(Some(to_proto::text_edit_vec(
934 TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
936 } else if *file == new_text {
937 // The document is already formatted correctly -- no edits needed.
940 Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
944 pub(crate) fn handle_code_action(
945 snap: GlobalStateSnapshot,
946 params: lsp_types::CodeActionParams,
947 ) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
948 let _p = profile::span("handle_code_action");
949 // We intentionally don't support command-based actions, as those either
950 // requires custom client-code anyway, or requires server-initiated edits.
951 // Server initiated edits break causality, so we avoid those as well.
952 if !snap.config.code_action_literals() {
956 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
957 let line_index = snap.file_line_index(file_id)?;
958 let range = from_proto::text_range(&line_index, params.range);
959 let frange = FileRange { file_id, range };
961 let mut assists_config = snap.config.assist();
962 assists_config.allowed = params
966 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
968 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
970 let include_quick_fixes = match ¶ms.context.only {
971 Some(v) => v.iter().any(|it| {
972 it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX
976 if include_quick_fixes {
977 add_quick_fixes(&snap, frange, &line_index, &mut res)?;
980 if snap.config.code_action_resolve() {
981 for (index, assist) in
982 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
984 res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
987 for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() {
988 res.push(to_proto::resolved_code_action(&snap, assist)?);
996 snap: &GlobalStateSnapshot,
998 line_index: &LineIndex,
999 acc: &mut Vec<lsp_ext::CodeAction>,
1001 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?;
1003 for fix in diagnostics
1005 .filter_map(|d| d.fix)
1006 .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some())
1008 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
1009 let action = lsp_ext::CodeAction {
1010 title: fix.label.to_string(),
1012 kind: Some(CodeActionKind::QUICKFIX),
1014 is_preferred: Some(false),
1020 for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
1021 let fix_range = from_proto::text_range(&line_index, fix.range);
1022 if fix_range.intersect(frange.range).is_some() {
1023 acc.push(fix.action.clone());
1029 pub(crate) fn handle_code_action_resolve(
1030 snap: GlobalStateSnapshot,
1031 mut code_action: lsp_ext::CodeAction,
1032 ) -> Result<lsp_ext::CodeAction> {
1033 let _p = profile::span("handle_code_action_resolve");
1034 let params = match code_action.data.take() {
1036 None => Err("can't resolve code action without data")?,
1039 let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?;
1040 let line_index = snap.file_line_index(file_id)?;
1041 let range = from_proto::text_range(&line_index, params.code_action_params.range);
1042 let frange = FileRange { file_id, range };
1044 let mut assists_config = snap.config.assist();
1045 assists_config.allowed = params
1049 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
1051 let assists = snap.analysis.assists(&assists_config, true, frange)?;
1052 let (id, index) = split_once(¶ms.id, ':').unwrap();
1053 let index = index.parse::<usize>().unwrap();
1054 let assist = &assists[index];
1055 assert!(assist.id.0 == id);
1056 let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
1057 code_action.edit = edit;
1061 pub(crate) fn handle_code_lens(
1062 snap: GlobalStateSnapshot,
1063 params: lsp_types::CodeLensParams,
1064 ) -> Result<Option<Vec<CodeLens>>> {
1065 let _p = profile::span("handle_code_lens");
1067 let lens_config = snap.config.lens();
1068 if lens_config.none() {
1069 // early return before any db query!
1070 return Ok(Some(Vec::default()));
1073 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
1074 let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1081 binary_target: cargo_target_spec
1085 TargetKind::Bin | TargetKind::Example | TargetKind::Test
1089 annotate_runnables: lens_config.runnable(),
1090 annotate_impls: lens_config.implementations,
1091 annotate_references: lens_config.refs,
1092 annotate_method_references: lens_config.method_refs,
1093 run: lens_config.run,
1094 debug: lens_config.debug,
1098 .map(|annotation| to_proto::code_lens(&snap, annotation).unwrap())
1104 pub(crate) fn handle_code_lens_resolve(
1105 snap: GlobalStateSnapshot,
1106 code_lens: CodeLens,
1107 ) -> Result<CodeLens> {
1108 let annotation = from_proto::annotation(&snap, code_lens)?;
1110 Ok(to_proto::code_lens(&snap, snap.analysis.resolve_annotation(annotation)?)?)
1113 pub(crate) fn handle_document_highlight(
1114 snap: GlobalStateSnapshot,
1115 params: lsp_types::DocumentHighlightParams,
1116 ) -> Result<Option<Vec<DocumentHighlight>>> {
1117 let _p = profile::span("handle_document_highlight");
1118 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1119 let line_index = snap.file_line_index(position.file_id)?;
1121 let refs = match snap
1123 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
1125 None => return Ok(None),
1129 let decl = if refs.declaration.nav.file_id == position.file_id {
1130 Some(DocumentHighlight {
1131 range: to_proto::range(&line_index, refs.declaration.nav.focus_or_full_range()),
1132 kind: refs.declaration.access.map(to_proto::document_highlight_kind),
1140 .get(&position.file_id)
1144 .map(|&(range, access)| DocumentHighlight {
1145 range: to_proto::range(&line_index, range),
1146 kind: access.map(to_proto::document_highlight_kind),
1151 .unwrap_or_default();
1155 pub(crate) fn handle_ssr(
1156 snap: GlobalStateSnapshot,
1157 params: lsp_ext::SsrParams,
1158 ) -> Result<lsp_types::WorkspaceEdit> {
1159 let _p = profile::span("handle_ssr");
1160 let selections = params
1163 .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
1164 .collect::<Result<Vec<_>, _>>()?;
1165 let position = from_proto::file_position(&snap, params.position)?;
1166 let source_change = snap.analysis.structural_search_replace(
1172 to_proto::workspace_edit(&snap, source_change)
1175 pub(crate) fn publish_diagnostics(
1176 snap: &GlobalStateSnapshot,
1178 ) -> Result<Vec<Diagnostic>> {
1179 let _p = profile::span("publish_diagnostics");
1180 let line_index = snap.file_line_index(file_id)?;
1182 let diagnostics: Vec<Diagnostic> = snap
1184 .diagnostics(&snap.config.diagnostics(), file_id)?
1186 .map(|d| Diagnostic {
1187 range: to_proto::range(&line_index, d.range),
1188 severity: Some(to_proto::diagnostic_severity(d.severity)),
1189 code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String),
1190 code_description: d.code.and_then(|code| {
1191 lsp_types::Url::parse(&format!(
1192 "https://rust-analyzer.github.io/manual.html#{}",
1196 .map(|href| lsp_types::CodeDescription { href })
1198 source: Some("rust-analyzer".to_string()),
1200 related_information: None,
1201 tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
1208 pub(crate) fn handle_inlay_hints(
1209 snap: GlobalStateSnapshot,
1210 params: InlayHintsParams,
1211 ) -> Result<Vec<InlayHint>> {
1212 let _p = profile::span("handle_inlay_hints");
1213 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
1214 let line_index = snap.file_line_index(file_id)?;
1217 .inlay_hints(file_id, &snap.config.inlay_hints())?
1219 .map(|it| to_proto::inlay_hint(&line_index, it))
1223 pub(crate) fn handle_call_hierarchy_prepare(
1224 snap: GlobalStateSnapshot,
1225 params: CallHierarchyPrepareParams,
1226 ) -> Result<Option<Vec<CallHierarchyItem>>> {
1227 let _p = profile::span("handle_call_hierarchy_prepare");
1228 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1230 let nav_info = match snap.analysis.call_hierarchy(position)? {
1231 None => return Ok(None),
1235 let RangeInfo { range: _, info: navs } = nav_info;
1238 .filter(|it| it.kind == Some(SymbolKind::Function))
1239 .map(|it| to_proto::call_hierarchy_item(&snap, it))
1240 .collect::<Result<Vec<_>>>()?;
1245 pub(crate) fn handle_call_hierarchy_incoming(
1246 snap: GlobalStateSnapshot,
1247 params: CallHierarchyIncomingCallsParams,
1248 ) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1249 let _p = profile::span("handle_call_hierarchy_incoming");
1250 let item = params.item;
1252 let doc = TextDocumentIdentifier::new(item.uri);
1253 let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1254 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1256 let call_items = match snap.analysis.incoming_calls(fpos)? {
1257 None => return Ok(None),
1261 let mut res = vec![];
1263 for call_item in call_items.into_iter() {
1264 let file_id = call_item.target.file_id;
1265 let line_index = snap.file_line_index(file_id)?;
1266 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1267 res.push(CallHierarchyIncomingCall {
1269 from_ranges: call_item
1272 .map(|it| to_proto::range(&line_index, it))
1280 pub(crate) fn handle_call_hierarchy_outgoing(
1281 snap: GlobalStateSnapshot,
1282 params: CallHierarchyOutgoingCallsParams,
1283 ) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1284 let _p = profile::span("handle_call_hierarchy_outgoing");
1285 let item = params.item;
1287 let doc = TextDocumentIdentifier::new(item.uri);
1288 let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1289 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1291 let call_items = match snap.analysis.outgoing_calls(fpos)? {
1292 None => return Ok(None),
1296 let mut res = vec![];
1298 for call_item in call_items.into_iter() {
1299 let file_id = call_item.target.file_id;
1300 let line_index = snap.file_line_index(file_id)?;
1301 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1302 res.push(CallHierarchyOutgoingCall {
1304 from_ranges: call_item
1307 .map(|it| to_proto::range(&line_index, it))
1315 pub(crate) fn handle_semantic_tokens_full(
1316 snap: GlobalStateSnapshot,
1317 params: SemanticTokensParams,
1318 ) -> Result<Option<SemanticTokensResult>> {
1319 let _p = profile::span("handle_semantic_tokens_full");
1321 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
1322 let text = snap.analysis.file_text(file_id)?;
1323 let line_index = snap.file_line_index(file_id)?;
1325 let highlights = snap.analysis.highlight(file_id)?;
1326 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1328 // Unconditionally cache the tokens
1329 snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
1331 Ok(Some(semantic_tokens.into()))
1334 pub(crate) fn handle_semantic_tokens_full_delta(
1335 snap: GlobalStateSnapshot,
1336 params: SemanticTokensDeltaParams,
1337 ) -> Result<Option<SemanticTokensFullDeltaResult>> {
1338 let _p = profile::span("handle_semantic_tokens_full_delta");
1340 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
1341 let text = snap.analysis.file_text(file_id)?;
1342 let line_index = snap.file_line_index(file_id)?;
1344 let highlights = snap.analysis.highlight(file_id)?;
1346 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1348 let mut cache = snap.semantic_tokens_cache.lock();
1349 let cached_tokens = cache.entry(params.text_document.uri).or_default();
1351 if let Some(prev_id) = &cached_tokens.result_id {
1352 if *prev_id == params.previous_result_id {
1353 let delta = to_proto::semantic_token_delta(&cached_tokens, &semantic_tokens);
1354 *cached_tokens = semantic_tokens;
1355 return Ok(Some(delta.into()));
1359 *cached_tokens = semantic_tokens.clone();
1361 Ok(Some(semantic_tokens.into()))
1364 pub(crate) fn handle_semantic_tokens_range(
1365 snap: GlobalStateSnapshot,
1366 params: SemanticTokensRangeParams,
1367 ) -> Result<Option<SemanticTokensRangeResult>> {
1368 let _p = profile::span("handle_semantic_tokens_range");
1370 let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1371 let text = snap.analysis.file_text(frange.file_id)?;
1372 let line_index = snap.file_line_index(frange.file_id)?;
1374 let highlights = snap.analysis.highlight_range(frange)?;
1375 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1376 Ok(Some(semantic_tokens.into()))
1379 pub(crate) fn handle_open_docs(
1380 snap: GlobalStateSnapshot,
1381 params: lsp_types::TextDocumentPositionParams,
1382 ) -> Result<Option<lsp_types::Url>> {
1383 let _p = profile::span("handle_open_docs");
1384 let position = from_proto::file_position(&snap, params)?;
1386 let remote = snap.analysis.external_docs(position)?;
1388 Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
1391 pub(crate) fn handle_open_cargo_toml(
1392 snap: GlobalStateSnapshot,
1393 params: lsp_ext::OpenCargoTomlParams,
1394 ) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
1395 let _p = profile::span("handle_open_cargo_toml");
1396 let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
1398 let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
1400 None => return Ok(None),
1403 let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
1404 let res: lsp_types::GotoDefinitionResponse =
1405 Location::new(cargo_toml_url, Range::default()).into();
1409 fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink {
1410 lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1413 fn show_impl_command_link(
1414 snap: &GlobalStateSnapshot,
1415 position: &FilePosition,
1416 ) -> Option<lsp_ext::CommandLinkGroup> {
1417 if snap.config.hover().implementations {
1418 if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
1419 let uri = to_proto::url(snap, position.file_id);
1420 let line_index = snap.file_line_index(position.file_id).ok()?;
1421 let position = to_proto::position(&line_index, position.offset);
1422 let locations: Vec<_> = nav_data
1425 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1427 let title = to_proto::implementation_title(locations.len());
1428 let command = to_proto::command::show_references(title, &uri, position, locations);
1430 return Some(lsp_ext::CommandLinkGroup {
1431 commands: vec![to_command_link(command, "Go to implementations".into())],
1432 ..Default::default()
1439 fn runnable_action_links(
1440 snap: &GlobalStateSnapshot,
1443 ) -> Option<lsp_ext::CommandLinkGroup> {
1444 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1445 let hover_config = snap.config.hover();
1446 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1450 let action: &'static _ = runnable.action();
1451 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1452 let mut group = lsp_ext::CommandLinkGroup::default();
1454 if hover_config.run {
1455 let run_command = to_proto::command::run_single(&r, action.run_title);
1456 group.commands.push(to_command_link(run_command, r.label.clone()));
1459 if hover_config.debug {
1460 let dbg_command = to_proto::command::debug_single(&r);
1461 group.commands.push(to_command_link(dbg_command, r.label));
1468 fn goto_type_action_links(
1469 snap: &GlobalStateSnapshot,
1470 nav_targets: &[HoverGotoTypeData],
1471 ) -> Option<lsp_ext::CommandLinkGroup> {
1472 if !snap.config.hover().goto_type_def || nav_targets.is_empty() {
1476 Some(lsp_ext::CommandLinkGroup {
1477 title: Some("Go to ".into()),
1478 commands: nav_targets
1481 to_proto::command::goto_location(snap, &it.nav)
1482 .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
1488 fn prepare_hover_actions(
1489 snap: &GlobalStateSnapshot,
1491 actions: &[HoverAction],
1492 ) -> Vec<lsp_ext::CommandLinkGroup> {
1493 if snap.config.hover().none() || !snap.config.hover_actions() {
1499 .filter_map(|it| match it {
1500 HoverAction::Implementation(position) => show_impl_command_link(snap, position),
1501 HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
1502 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1507 fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
1508 match runnable.kind {
1509 RunnableKind::Bin => {
1510 // Do not suggest binary run on other target than binary
1512 Some(spec) => !matches!(
1514 TargetKind::Bin | TargetKind::Example | TargetKind::Test
1523 #[derive(Debug, Serialize, Deserialize)]
1524 struct CompletionResolveData {
1525 position: lsp_types::TextDocumentPositionParams,
1526 full_import_path: String,
1527 imported_name: String,
1528 import_for_trait_assoc_item: bool,
1531 fn fill_resolve_data(
1532 resolve_data: &mut Option<serde_json::Value>,
1533 item: &ide::CompletionItem,
1534 position: &TextDocumentPositionParams,
1536 let import_edit = item.import_to_add()?;
1537 let full_import_path = import_edit.import_path.to_string();
1538 let imported_name = import_edit.import_path.segments().last()?.to_string();
1540 *resolve_data = Some(
1541 to_value(CompletionResolveData {
1542 position: position.to_owned(),
1545 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,