aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/main_loop/handlers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_lsp_server/src/main_loop/handlers.rs')
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs121
1 files changed, 60 insertions, 61 deletions
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 8e43f0575..65e8bc856 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -1,17 +1,22 @@
1//! This module is responsible for implementing handlers for Lanuage Server Protocol. 1//! This module is responsible for implementing handlers for Lanuage Server Protocol.
2//! The majority of requests are fulfilled by calling into the `ra_ide` crate. 2//! The majority of requests are fulfilled by calling into the `ra_ide` crate.
3 3
4use std::{fmt::Write as _, io::Write as _}; 4use std::{
5 collections::hash_map::Entry,
6 fmt::Write as _,
7 io::Write as _,
8 process::{self, Stdio},
9};
5 10
6use either::Either;
7use lsp_server::ErrorCode; 11use lsp_server::ErrorCode;
8use lsp_types::{ 12use lsp_types::{
9 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 13 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
10 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 14 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
11 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 15 CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem,
12 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 16 Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
13 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 17 FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
14 Range, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, 18 PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier,
19 TextEdit, WorkspaceEdit,
15}; 20};
16use ra_ide::{ 21use ra_ide::{
17 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, 22 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -29,6 +34,7 @@ use crate::{
29 to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, 34 to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith,
30 TryConvWithToVec, 35 TryConvWithToVec,
31 }, 36 },
37 diagnostics::DiagnosticTask,
32 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, 38 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind},
33 world::WorldSnapshot, 39 world::WorldSnapshot,
34 LspError, Result, 40 LspError, Result,
@@ -582,21 +588,19 @@ pub fn handle_formatting(
582 let file_line_index = world.analysis().file_line_index(file_id)?; 588 let file_line_index = world.analysis().file_line_index(file_id)?;
583 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); 589 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
584 590
585 use std::process;
586 let mut rustfmt = process::Command::new("rustfmt"); 591 let mut rustfmt = process::Command::new("rustfmt");
587 if let Some(&crate_id) = crate_ids.first() { 592 if let Some(&crate_id) = crate_ids.first() {
588 // Assume all crates are in the same edition 593 // Assume all crates are in the same edition
589 let edition = world.analysis().crate_edition(crate_id)?; 594 let edition = world.analysis().crate_edition(crate_id)?;
590 rustfmt.args(&["--edition", &edition.to_string()]); 595 rustfmt.args(&["--edition", &edition.to_string()]);
591 } 596 }
592 rustfmt.stdin(process::Stdio::piped()).stdout(process::Stdio::piped());
593 597
594 if let Ok(path) = params.text_document.uri.to_file_path() { 598 if let Ok(path) = params.text_document.uri.to_file_path() {
595 if let Some(parent) = path.parent() { 599 if let Some(parent) = path.parent() {
596 rustfmt.current_dir(parent); 600 rustfmt.current_dir(parent);
597 } 601 }
598 } 602 }
599 let mut rustfmt = rustfmt.spawn()?; 603 let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
600 604
601 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; 605 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
602 606
@@ -674,59 +678,61 @@ pub fn handle_code_action(
674 res.push(action.into()); 678 res.push(action.into());
675 } 679 }
676 680
677 for fix in world.check_watcher.read().fixes_for(&params.text_document.uri).into_iter().flatten() 681 for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
678 { 682 let fix_range = fix.range.conv_with(&line_index);
679 let fix_range = fix.location.range.conv_with(&line_index);
680 if fix_range.intersection(&range).is_none() { 683 if fix_range.intersection(&range).is_none() {
681 continue; 684 continue;
682 } 685 }
686 res.push(fix.action.clone());
687 }
683 688
684 let edit = { 689 let mut groups = FxHashMap::default();
685 let edits = vec![TextEdit::new(fix.location.range, fix.replacement.clone())]; 690 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() {
686 let mut edit_map = std::collections::HashMap::new(); 691 let arg = to_value(assist.source_change.try_conv_with(&world)?)?;
687 edit_map.insert(fix.location.uri.clone(), edits); 692
688 WorkspaceEdit::new(edit_map) 693 let (command, title, arg) = match assist.group_label {
694 None => ("rust-analyzer.applySourceChange", assist.label.clone(), arg),
695
696 // Group all assists with the same `group_label` into a single CodeAction.
697 Some(group_label) => {
698 match groups.entry(group_label.clone()) {
699 Entry::Occupied(entry) => {
700 let idx: usize = *entry.get();
701 match &mut res[idx] {
702 CodeActionOrCommand::CodeAction(CodeAction {
703 command: Some(Command { arguments: Some(arguments), .. }),
704 ..
705 }) => match arguments.as_mut_slice() {
706 [serde_json::Value::Array(arguments)] => arguments.push(arg),
707 _ => panic!("invalid group"),
708 },
709 _ => panic!("invalid group"),
710 }
711 continue;
712 }
713 Entry::Vacant(entry) => {
714 entry.insert(res.len());
715 }
716 }
717 ("rust-analyzer.selectAndApplySourceChange", group_label, to_value(vec![arg])?)
718 }
689 }; 719 };
690 720
691 let action = CodeAction { 721 let command = Command {
692 title: fix.title.clone(), 722 title: assist.label.clone(),
693 kind: Some("quickfix".to_string()), 723 command: command.to_string(),
694 diagnostics: Some(fix.diagnostics.clone()), 724 arguments: Some(vec![arg]),
695 edit: Some(edit),
696 command: None,
697 is_preferred: None,
698 }; 725 };
699 res.push(action.into());
700 }
701 726
702 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { 727 let kind = match assist.id {
703 let title = assist.label.clone(); 728 AssistId("introduce_variable") => Some("refactor.extract.variable".to_string()),
704 729 AssistId("add_custom_impl") => Some("refactor.rewrite.add_custom_impl".to_string()),
705 let command = match assist.change_data { 730 _ => None,
706 Either::Left(change) => Command {
707 title,
708 command: "rust-analyzer.applySourceChange".to_string(),
709 arguments: Some(vec![to_value(change.try_conv_with(&world)?)?]),
710 },
711 Either::Right(changes) => Command {
712 title,
713 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
714 arguments: Some(vec![to_value(
715 changes
716 .into_iter()
717 .map(|change| change.try_conv_with(&world))
718 .collect::<Result<Vec<_>>>()?,
719 )?]),
720 },
721 }; 731 };
722 732
723 let action = CodeAction { 733 let action = CodeAction {
724 title: command.title.clone(), 734 title,
725 kind: match assist.id { 735 kind,
726 AssistId("introduce_variable") => Some("refactor.extract.variable".to_string()),
727 AssistId("add_custom_impl") => Some("refactor.rewrite.add_custom_impl".to_string()),
728 _ => None,
729 },
730 diagnostics: None, 736 diagnostics: None,
731 edit: None, 737 edit: None,
732 command: Some(command), 738 command: Some(command),
@@ -874,14 +880,10 @@ pub fn handle_document_highlight(
874 )) 880 ))
875} 881}
876 882
877pub fn publish_diagnostics( 883pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
878 world: &WorldSnapshot,
879 file_id: FileId,
880) -> Result<req::PublishDiagnosticsParams> {
881 let _p = profile("publish_diagnostics"); 884 let _p = profile("publish_diagnostics");
882 let uri = world.file_id_to_uri(file_id)?;
883 let line_index = world.analysis().file_line_index(file_id)?; 885 let line_index = world.analysis().file_line_index(file_id)?;
884 let mut diagnostics: Vec<Diagnostic> = world 886 let diagnostics: Vec<Diagnostic> = world
885 .analysis() 887 .analysis()
886 .diagnostics(file_id)? 888 .diagnostics(file_id)?
887 .into_iter() 889 .into_iter()
@@ -895,10 +897,7 @@ pub fn publish_diagnostics(
895 tags: None, 897 tags: None,
896 }) 898 })
897 .collect(); 899 .collect();
898 if let Some(check_diags) = world.check_watcher.read().diagnostics_for(&uri) { 900 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
899 diagnostics.extend(check_diags.iter().cloned());
900 }
901 Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None })
902} 901}
903 902
904pub fn publish_decorations( 903pub fn publish_decorations(