diff options
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 | ||
4 | use std::{collections::HashMap, sync::Arc}; | 4 | use std::{collections::HashMap, sync::Arc}; |
5 | 5 | ||
6 | use lsp_types::{CodeActionOrCommand, Diagnostic, Range}; | 6 | use lsp_types::{Diagnostic, Range}; |
7 | use ra_ide::FileId; | 7 | use ra_ide::FileId; |
8 | 8 | ||
9 | use crate::lsp_ext; | ||
10 | |||
9 | pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; | 11 | pub 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)] |
19 | pub struct Fix { | 21 | pub 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)] |
25 | pub enum DiagnosticTask { | 27 | pub 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 | ||
9 | use lsp_types::{ | 9 | use 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 | }; |
13 | use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; | 13 | use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; |
14 | use stdx::format_to; | 14 | use stdx::format_to; |
15 | 15 | ||
16 | use crate::Result; | 16 | use 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 |
19 | fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { | 19 | fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { |
@@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool { | |||
110 | 110 | ||
111 | enum MappedRustChildDiagnostic { | 111 | enum 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( | |||
158 | pub(crate) struct MappedRustDiagnostic { | 160 | pub(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 | ||
3 | use std::path::PathBuf; | 3 | use std::{collections::HashMap, path::PathBuf}; |
4 | 4 | ||
5 | use lsp_types::request::Request; | 5 | use lsp_types::request::Request; |
6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; | 6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; |
@@ -137,7 +137,7 @@ pub struct Runnable { | |||
137 | #[serde(rename_all = "camelCase")] | 137 | #[serde(rename_all = "camelCase")] |
138 | pub struct SourceChange { | 138 | pub 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 | |||
187 | pub enum CodeActionRequest {} | ||
188 | |||
189 | impl 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)] | ||
196 | pub 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")] | ||
208 | pub 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")] | ||
215 | pub 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")] | ||
222 | pub 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")] | ||
229 | pub 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; | |||
11 | use lsp_types::{ | 11 | use 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 | }; |
21 | use ra_ide::{ | 20 | use 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 | ||
593 | pub fn handle_references( | 591 | pub fn handle_references( |
@@ -696,14 +694,21 @@ pub fn handle_formatting( | |||
696 | pub fn handle_code_action( | 694 | pub 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, ¶ms.text_document.uri)?; | 706 | let file_id = from_proto::file_id(&world, ¶ms.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 | ||
115 | pub(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 | |||
115 | pub(crate) fn text_edit_vec( | 131 | pub(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 | ||
444 | pub(crate) fn text_document_edit( | 460 | pub(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 | ||
460 | pub(crate) fn resource_op( | 477 | pub(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 | |||
525 | pub(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 | |||
543 | pub(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 | |||
551 | impl 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 | ||
519 | pub fn call_hierarchy_item( | 586 | pub fn call_hierarchy_item( |
@@ -571,22 +638,25 @@ fn main() <fold>{ | |||
571 | } | 638 | } |
572 | } | 639 | } |
573 | 640 | ||
574 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> { | 641 | pub(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 | } |