aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assist_context.rs10
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs10
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap6
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap6
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs20
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs53
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs82
-rw-r--r--crates/rust-analyzer/src/to_proto.rs130
9 files changed, 200 insertions, 119 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index b90bbf8b2..0dcd9df61 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -208,16 +208,6 @@ impl AssistBuilder {
208 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { 208 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
209 self.edit.replace(range, replace_with.into()) 209 self.edit.replace(range, replace_with.into())
210 } 210 }
211 /// Append specified `text` at the given `offset`
212 pub(crate) fn replace_snippet(
213 &mut self,
214 _cap: SnippetCap,
215 range: TextRange,
216 replace_with: impl Into<String>,
217 ) {
218 self.is_snippet = true;
219 self.edit.replace(range, replace_with.into())
220 }
221 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { 211 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
222 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 212 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
223 } 213 }
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 4bdd45a7d..25856c543 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -3,9 +3,11 @@ pub(crate) mod to_proto;
3 3
4use std::{collections::HashMap, sync::Arc}; 4use std::{collections::HashMap, sync::Arc};
5 5
6use lsp_types::{CodeActionOrCommand, Diagnostic, Range}; 6use lsp_types::{Diagnostic, Range};
7use ra_ide::FileId; 7use ra_ide::FileId;
8 8
9use crate::lsp_ext;
10
9pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; 11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
10 12
11#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
@@ -18,13 +20,13 @@ pub struct DiagnosticCollection {
18#[derive(Debug, Clone)] 20#[derive(Debug, Clone)]
19pub struct Fix { 21pub struct Fix {
20 pub range: Range, 22 pub range: Range,
21 pub action: CodeActionOrCommand, 23 pub action: lsp_ext::CodeAction,
22} 24}
23 25
24#[derive(Debug)] 26#[derive(Debug)]
25pub enum DiagnosticTask { 27pub enum DiagnosticTask {
26 ClearCheck, 28 ClearCheck,
27 AddCheck(FileId, Diagnostic, Vec<CodeActionOrCommand>), 29 AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
28 SetNative(FileId, Vec<Diagnostic>), 30 SetNative(FileId, Vec<Diagnostic>),
29} 31}
30 32
@@ -38,7 +40,7 @@ impl DiagnosticCollection {
38 &mut self, 40 &mut self,
39 file_id: FileId, 41 file_id: FileId,
40 diagnostic: Diagnostic, 42 diagnostic: Diagnostic,
41 fixes: Vec<CodeActionOrCommand>, 43 fixes: Vec<lsp_ext::CodeAction>,
42 ) { 44 ) {
43 let diagnostics = self.check.entry(file_id).or_default(); 45 let diagnostics = self.check.entry(file_id).or_default();
44 for existing_diagnostic in diagnostics.iter() { 46 for existing_diagnostic in diagnostics.iter() {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
index 076b3cf27..96466b5c9 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -68,9 +68,9 @@ expression: diag
68 kind: Some( 68 kind: Some(
69 "quickfix", 69 "quickfix",
70 ), 70 ),
71 diagnostics: None, 71 command: None,
72 edit: Some( 72 edit: Some(
73 WorkspaceEdit { 73 SnippetWorkspaceEdit {
74 changes: Some( 74 changes: Some(
75 { 75 {
76 "file:///test/src/main.rs": [ 76 "file:///test/src/main.rs": [
@@ -106,8 +106,6 @@ expression: diag
106 document_changes: None, 106 document_changes: None,
107 }, 107 },
108 ), 108 ),
109 command: None,
110 is_preferred: None,
111 }, 109 },
112 ], 110 ],
113 }, 111 },
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 69138c15b..8f962277f 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -53,9 +53,9 @@ expression: diag
53 kind: Some( 53 kind: Some(
54 "quickfix", 54 "quickfix",
55 ), 55 ),
56 diagnostics: None, 56 command: None,
57 edit: Some( 57 edit: Some(
58 WorkspaceEdit { 58 SnippetWorkspaceEdit {
59 changes: Some( 59 changes: Some(
60 { 60 {
61 "file:///test/driver/subcommand/repl.rs": [ 61 "file:///test/driver/subcommand/repl.rs": [
@@ -78,8 +78,6 @@ expression: diag
78 document_changes: None, 78 document_changes: None,
79 }, 79 },
80 ), 80 ),
81 command: None,
82 is_preferred: None,
83 }, 81 },
84 ], 82 ],
85 }, 83 },
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index eabf4908f..afea59525 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -7,13 +7,13 @@ use std::{
7}; 7};
8 8
9use lsp_types::{ 9use lsp_types::{
10 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, 10 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
11 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, 11 NumberOrString, Position, Range, TextEdit, Url,
12}; 12};
13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
14use stdx::format_to; 14use stdx::format_to;
15 15
16use crate::Result; 16use crate::{lsp_ext, Result};
17 17
18/// Converts a Rust level string to a LSP severity 18/// Converts a Rust level string to a LSP severity
19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { 19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
@@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
110 110
111enum MappedRustChildDiagnostic { 111enum MappedRustChildDiagnostic {
112 Related(DiagnosticRelatedInformation), 112 Related(DiagnosticRelatedInformation),
113 SuggestedFix(CodeAction), 113 SuggestedFix(lsp_ext::CodeAction),
114 MessageLine(String), 114 MessageLine(String),
115} 115}
116 116
@@ -143,13 +143,15 @@ fn map_rust_child_diagnostic(
143 message: rd.message.clone(), 143 message: rd.message.clone(),
144 }) 144 })
145 } else { 145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(CodeAction { 146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147 title: rd.message.clone(), 147 title: rd.message.clone(),
148 kind: Some("quickfix".to_string()), 148 kind: Some("quickfix".to_string()),
149 diagnostics: None, 149 edit: Some(lsp_ext::SnippetWorkspaceEdit {
150 edit: Some(WorkspaceEdit::new(edit_map)), 150 // FIXME: there's no good reason to use edit_map here....
151 changes: Some(edit_map),
152 document_changes: None,
153 }),
151 command: None, 154 command: None,
152 is_preferred: None,
153 }) 155 })
154 } 156 }
155} 157}
@@ -158,7 +160,7 @@ fn map_rust_child_diagnostic(
158pub(crate) struct MappedRustDiagnostic { 160pub(crate) struct MappedRustDiagnostic {
159 pub location: Location, 161 pub location: Location,
160 pub diagnostic: Diagnostic, 162 pub diagnostic: Diagnostic,
161 pub fixes: Vec<CodeAction>, 163 pub fixes: Vec<lsp_ext::CodeAction>,
162} 164}
163 165
164/// Converts a Rust root diagnostic to LSP form 166/// Converts a Rust root diagnostic to LSP form
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 313a8c769..c7a3a6911 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -1,6 +1,6 @@
1//! rust-analyzer extensions to the LSP. 1//! rust-analyzer extensions to the LSP.
2 2
3use std::path::PathBuf; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
@@ -137,7 +137,7 @@ pub struct Runnable {
137#[serde(rename_all = "camelCase")] 137#[serde(rename_all = "camelCase")]
138pub struct SourceChange { 138pub struct SourceChange {
139 pub label: String, 139 pub label: String,
140 pub workspace_edit: lsp_types::WorkspaceEdit, 140 pub workspace_edit: SnippetWorkspaceEdit,
141 pub cursor_position: Option<lsp_types::TextDocumentPositionParams>, 141 pub cursor_position: Option<lsp_types::TextDocumentPositionParams>,
142} 142}
143 143
@@ -183,3 +183,52 @@ pub struct SsrParams {
183 pub query: String, 183 pub query: String,
184 pub parse_only: bool, 184 pub parse_only: bool,
185} 185}
186
187pub enum CodeActionRequest {}
188
189impl Request for CodeActionRequest {
190 type Params = lsp_types::CodeActionParams;
191 type Result = Option<Vec<CodeAction>>;
192 const METHOD: &'static str = "textDocument/codeAction";
193}
194
195#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
196pub struct CodeAction {
197 pub title: String,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub kind: Option<String>,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub command: Option<lsp_types::Command>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub edit: Option<SnippetWorkspaceEdit>,
204}
205
206#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
207#[serde(rename_all = "camelCase")]
208pub struct SnippetWorkspaceEdit {
209 pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
210 pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
211}
212
213#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
214#[serde(untagged, rename_all = "lowercase")]
215pub enum SnippetDocumentChangeOperation {
216 Op(lsp_types::ResourceOp),
217 Edit(SnippetTextDocumentEdit),
218}
219
220#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
221#[serde(rename_all = "camelCase")]
222pub struct SnippetTextDocumentEdit {
223 pub text_document: lsp_types::VersionedTextDocumentIdentifier,
224 pub edits: Vec<SnippetTextEdit>,
225}
226
227#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
228#[serde(rename_all = "camelCase")]
229pub struct SnippetTextEdit {
230 pub range: Range,
231 pub new_text: String,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
234}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 15e5bb354..87795fffb 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -518,6 +518,7 @@ fn on_request(
518 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)? 518 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
519 .on::<lsp_ext::Runnables>(handlers::handle_runnables)? 519 .on::<lsp_ext::Runnables>(handlers::handle_runnables)?
520 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? 520 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
521 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
521 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? 522 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
522 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? 523 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
523 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 524 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
@@ -525,7 +526,6 @@ fn on_request(
525 .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)? 526 .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
526 .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)? 527 .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
527 .on::<lsp_types::request::Completion>(handlers::handle_completion)? 528 .on::<lsp_types::request::Completion>(handlers::handle_completion)?
528 .on::<lsp_types::request::CodeActionRequest>(handlers::handle_code_action)?
529 .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)? 529 .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
530 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? 530 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
531 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? 531 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 13ae061fa..4ff8fa69e 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -11,12 +11,11 @@ use lsp_server::ErrorCode;
11use lsp_types::{ 11use lsp_types::{
12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
15 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit,
19 TextEdit, Url, WorkspaceEdit,
20}; 19};
21use ra_ide::{ 20use ra_ide::{
22 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
@@ -585,9 +584,8 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
585 None => return Ok(None), 584 None => return Ok(None),
586 Some(it) => it.info, 585 Some(it) => it.info,
587 }; 586 };
588 587 let workspace_edit = to_proto::workspace_edit(&world, source_change)?;
589 let source_change = to_proto::source_change(&world, source_change)?; 588 Ok(Some(workspace_edit))
590 Ok(Some(source_change.workspace_edit))
591} 589}
592 590
593pub fn handle_references( 591pub fn handle_references(
@@ -696,14 +694,21 @@ pub fn handle_formatting(
696pub fn handle_code_action( 694pub fn handle_code_action(
697 world: WorldSnapshot, 695 world: WorldSnapshot,
698 params: lsp_types::CodeActionParams, 696 params: lsp_types::CodeActionParams,
699) -> Result<Option<CodeActionResponse>> { 697) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
700 let _p = profile("handle_code_action"); 698 let _p = profile("handle_code_action");
699 // We intentionally don't support command-based actions, as those either
700 // requires custom client-code anyway, or requires server-initiated edits.
701 // Server initiated edits break causality, so we avoid those as well.
702 if !world.config.client_caps.code_action_literals {
703 return Ok(None);
704 }
705
701 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 706 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
702 let line_index = world.analysis().file_line_index(file_id)?; 707 let line_index = world.analysis().file_line_index(file_id)?;
703 let range = from_proto::text_range(&line_index, params.range); 708 let range = from_proto::text_range(&line_index, params.range);
704 709
705 let diagnostics = world.analysis().diagnostics(file_id)?; 710 let diagnostics = world.analysis().diagnostics(file_id)?;
706 let mut res = CodeActionResponse::default(); 711 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
707 712
708 let fixes_from_diagnostics = diagnostics 713 let fixes_from_diagnostics = diagnostics
709 .into_iter() 714 .into_iter()
@@ -713,22 +718,9 @@ pub fn handle_code_action(
713 718
714 for source_edit in fixes_from_diagnostics { 719 for source_edit in fixes_from_diagnostics {
715 let title = source_edit.label.clone(); 720 let title = source_edit.label.clone();
716 let edit = to_proto::source_change(&world, source_edit)?; 721 let edit = to_proto::snippet_workspace_edit(&world, source_edit)?;
717 722 let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None };
718 let command = Command { 723 res.push(action);
719 title,
720 command: "rust-analyzer.applySourceChange".to_string(),
721 arguments: Some(vec![to_value(edit).unwrap()]),
722 };
723 let action = CodeAction {
724 title: command.title.clone(),
725 kind: None,
726 diagnostics: None,
727 edit: None,
728 command: Some(command),
729 is_preferred: None,
730 };
731 res.push(action.into());
732 } 724 }
733 725
734 for fix in world.check_fixes.get(&file_id).into_iter().flatten() { 726 for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
@@ -748,8 +740,13 @@ pub fn handle_code_action(
748 .entry(label.to_owned()) 740 .entry(label.to_owned())
749 .or_insert_with(|| { 741 .or_insert_with(|| {
750 let idx = res.len(); 742 let idx = res.len();
751 let dummy = Command::new(String::new(), String::new(), None); 743 let dummy = lsp_ext::CodeAction {
752 res.push(dummy.into()); 744 title: String::new(),
745 kind: None,
746 command: None,
747 edit: None,
748 };
749 res.push(dummy);
753 (idx, Vec::new()) 750 (idx, Vec::new())
754 }) 751 })
755 .1 752 .1
@@ -777,35 +774,10 @@ pub fn handle_code_action(
777 command: "rust-analyzer.selectAndApplySourceChange".to_string(), 774 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
778 arguments: Some(vec![serde_json::Value::Array(arguments)]), 775 arguments: Some(vec![serde_json::Value::Array(arguments)]),
779 }); 776 });
780 res[idx] = CodeAction { 777 res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command };
781 title,
782 kind: None,
783 diagnostics: None,
784 edit: None,
785 command,
786 is_preferred: None,
787 }
788 .into();
789 } 778 }
790 } 779 }
791 780
792 // If the client only supports commands then filter the list
793 // and remove and actions that depend on edits.
794 if !world.config.client_caps.code_action_literals {
795 // FIXME: use drain_filter once it hits stable.
796 res = res
797 .into_iter()
798 .filter_map(|it| match it {
799 cmd @ lsp_types::CodeActionOrCommand::Command(_) => Some(cmd),
800 lsp_types::CodeActionOrCommand::CodeAction(action) => match action.command {
801 Some(cmd) if action.edit.is_none() => {
802 Some(lsp_types::CodeActionOrCommand::Command(cmd))
803 }
804 _ => None,
805 },
806 })
807 .collect();
808 }
809 Ok(Some(res)) 781 Ok(Some(res))
810} 782}
811 783
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a8e2e535f..2b1a3378f 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -112,6 +112,22 @@ pub(crate) fn text_edit(
112 lsp_types::TextEdit { range, new_text } 112 lsp_types::TextEdit { range, new_text }
113} 113}
114 114
115pub(crate) fn snippet_text_edit(
116 line_index: &LineIndex,
117 line_endings: LineEndings,
118 is_snippet: bool,
119 indel: Indel,
120) -> lsp_ext::SnippetTextEdit {
121 let text_edit = text_edit(line_index, line_endings, indel);
122 let insert_text_format =
123 if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None };
124 lsp_ext::SnippetTextEdit {
125 range: text_edit.range,
126 new_text: text_edit.new_text,
127 insert_text_format,
128 }
129}
130
115pub(crate) fn text_edit_vec( 131pub(crate) fn text_edit_vec(
116 line_index: &LineIndex, 132 line_index: &LineIndex,
117 line_endings: LineEndings, 133 line_endings: LineEndings,
@@ -441,10 +457,11 @@ pub(crate) fn goto_definition_response(
441 } 457 }
442} 458}
443 459
444pub(crate) fn text_document_edit( 460pub(crate) fn snippet_text_document_edit(
445 world: &WorldSnapshot, 461 world: &WorldSnapshot,
462 is_snippet: bool,
446 source_file_edit: SourceFileEdit, 463 source_file_edit: SourceFileEdit,
447) -> Result<lsp_types::TextDocumentEdit> { 464) -> Result<lsp_ext::SnippetTextDocumentEdit> {
448 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; 465 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?;
449 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; 466 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?;
450 let line_endings = world.file_line_endings(source_file_edit.file_id); 467 let line_endings = world.file_line_endings(source_file_edit.file_id);
@@ -452,9 +469,9 @@ pub(crate) fn text_document_edit(
452 .edit 469 .edit
453 .as_indels() 470 .as_indels()
454 .iter() 471 .iter()
455 .map(|it| text_edit(&line_index, line_endings, it.clone())) 472 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone()))
456 .collect(); 473 .collect();
457 Ok(lsp_types::TextDocumentEdit { text_document, edits }) 474 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
458} 475}
459 476
460pub(crate) fn resource_op( 477pub(crate) fn resource_op(
@@ -500,20 +517,70 @@ pub(crate) fn source_change(
500 }) 517 })
501 } 518 }
502 }; 519 };
503 let mut document_changes: Vec<lsp_types::DocumentChangeOperation> = Vec::new(); 520 let label = source_change.label.clone();
521 let workspace_edit = self::snippet_workspace_edit(world, source_change)?;
522 Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position })
523}
524
525pub(crate) fn snippet_workspace_edit(
526 world: &WorldSnapshot,
527 source_change: SourceChange,
528) -> Result<lsp_ext::SnippetWorkspaceEdit> {
529 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
504 for op in source_change.file_system_edits { 530 for op in source_change.file_system_edits {
505 let op = resource_op(&world, op)?; 531 let op = resource_op(&world, op)?;
506 document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); 532 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
507 } 533 }
508 for edit in source_change.source_file_edits { 534 for edit in source_change.source_file_edits {
509 let edit = text_document_edit(&world, edit)?; 535 let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?;
510 document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); 536 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
537 }
538 let workspace_edit =
539 lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) };
540 Ok(workspace_edit)
541}
542
543pub(crate) fn workspace_edit(
544 world: &WorldSnapshot,
545 source_change: SourceChange,
546) -> Result<lsp_types::WorkspaceEdit> {
547 assert!(!source_change.is_snippet);
548 snippet_workspace_edit(world, source_change).map(|it| it.into())
549}
550
551impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
552 fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit {
553 lsp_types::WorkspaceEdit {
554 changes: None,
555 document_changes: snippet_workspace_edit.document_changes.map(|changes| {
556 lsp_types::DocumentChanges::Operations(
557 changes
558 .into_iter()
559 .map(|change| match change {
560 lsp_ext::SnippetDocumentChangeOperation::Op(op) => {
561 lsp_types::DocumentChangeOperation::Op(op)
562 }
563 lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => {
564 lsp_types::DocumentChangeOperation::Edit(
565 lsp_types::TextDocumentEdit {
566 text_document: edit.text_document,
567 edits: edit
568 .edits
569 .into_iter()
570 .map(|edit| lsp_types::TextEdit {
571 range: edit.range,
572 new_text: edit.new_text,
573 })
574 .collect(),
575 },
576 )
577 }
578 })
579 .collect(),
580 )
581 }),
582 }
511 } 583 }
512 let workspace_edit = lsp_types::WorkspaceEdit {
513 changes: None,
514 document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)),
515 };
516 Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position })
517} 584}
518 585
519pub fn call_hierarchy_item( 586pub fn call_hierarchy_item(
@@ -571,22 +638,25 @@ fn main() <fold>{
571 } 638 }
572} 639}
573 640
574pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> { 641pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> {
575 let source_change = source_change(&world, assist.source_change)?; 642 let res = if assist.source_change.is_snippet {
576 let arg = serde_json::to_value(source_change)?; 643 lsp_ext::CodeAction {
577 let title = assist.label; 644 title: assist.label,
578 let command = lsp_types::Command { 645 kind: Some(String::new()),
579 title: title.clone(), 646 edit: Some(snippet_workspace_edit(world, assist.source_change)?),
580 command: "rust-analyzer.applySourceChange".to_string(), 647 command: None,
581 arguments: Some(vec![arg]), 648 }
582 }; 649 } else {
650 let source_change = source_change(&world, assist.source_change)?;
651 let arg = serde_json::to_value(source_change)?;
652 let title = assist.label;
653 let command = lsp_types::Command {
654 title: title.clone(),
655 command: "rust-analyzer.applySourceChange".to_string(),
656 arguments: Some(vec![arg]),
657 };
583 658
584 Ok(lsp_types::CodeAction { 659 lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) }
585 title, 660 };
586 kind: Some(String::new()), 661 Ok(res)
587 diagnostics: None,
588 edit: None,
589 command: Some(command),
590 is_preferred: None,
591 })
592} 662}