Skip to content

Commit 331a0c6

Browse files
committed
Server side of SnippetTextEdit
1 parent ff52e1c commit 331a0c6

9 files changed

+200
-119
lines changed

crates/ra_assists/src/assist_context.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,6 @@ impl AssistBuilder {
208208
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
209209
self.edit.replace(range, replace_with.into())
210210
}
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-
}
221211
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
222212
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
223213
}

crates/rust-analyzer/src/diagnostics.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ pub(crate) mod to_proto;
33

44
use std::{collections::HashMap, sync::Arc};
55

6-
use lsp_types::{CodeActionOrCommand, Diagnostic, Range};
6+
use lsp_types::{Diagnostic, Range};
77
use ra_ide::FileId;
88

9+
use crate::lsp_ext;
10+
911
pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
1012

1113
#[derive(Debug, Default, Clone)]
@@ -18,13 +20,13 @@ pub struct DiagnosticCollection {
1820
#[derive(Debug, Clone)]
1921
pub struct Fix {
2022
pub range: Range,
21-
pub action: CodeActionOrCommand,
23+
pub action: lsp_ext::CodeAction,
2224
}
2325

2426
#[derive(Debug)]
2527
pub enum DiagnosticTask {
2628
ClearCheck,
27-
AddCheck(FileId, Diagnostic, Vec<CodeActionOrCommand>),
29+
AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
2830
SetNative(FileId, Vec<Diagnostic>),
2931
}
3032

@@ -38,7 +40,7 @@ impl DiagnosticCollection {
3840
&mut self,
3941
file_id: FileId,
4042
diagnostic: Diagnostic,
41-
fixes: Vec<CodeActionOrCommand>,
43+
fixes: Vec<lsp_ext::CodeAction>,
4244
) {
4345
let diagnostics = self.check.entry(file_id).or_default();
4446
for existing_diagnostic in diagnostics.iter() {

crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ expression: diag
6868
kind: Some(
6969
"quickfix",
7070
),
71-
diagnostics: None,
71+
command: None,
7272
edit: Some(
73-
WorkspaceEdit {
73+
SnippetWorkspaceEdit {
7474
changes: Some(
7575
{
7676
"file:///test/src/main.rs": [
@@ -106,8 +106,6 @@ expression: diag
106106
document_changes: None,
107107
},
108108
),
109-
command: None,
110-
is_preferred: None,
111109
},
112110
],
113111
},

crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ expression: diag
5353
kind: Some(
5454
"quickfix",
5555
),
56-
diagnostics: None,
56+
command: None,
5757
edit: Some(
58-
WorkspaceEdit {
58+
SnippetWorkspaceEdit {
5959
changes: Some(
6060
{
6161
"file:///test/driver/subcommand/repl.rs": [
@@ -78,8 +78,6 @@ expression: diag
7878
document_changes: None,
7979
},
8080
),
81-
command: None,
82-
is_preferred: None,
8381
},
8482
],
8583
},

crates/rust-analyzer/src/diagnostics/to_proto.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ use std::{
77
};
88

99
use lsp_types::{
10-
CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag,
11-
Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
10+
Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
11+
NumberOrString, Position, Range, TextEdit, Url,
1212
};
1313
use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
1414
use stdx::format_to;
1515

16-
use crate::Result;
16+
use crate::{lsp_ext, Result};
1717

1818
/// Converts a Rust level string to a LSP severity
1919
fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
@@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
110110

111111
enum MappedRustChildDiagnostic {
112112
Related(DiagnosticRelatedInformation),
113-
SuggestedFix(CodeAction),
113+
SuggestedFix(lsp_ext::CodeAction),
114114
MessageLine(String),
115115
}
116116

@@ -143,13 +143,15 @@ fn map_rust_child_diagnostic(
143143
message: rd.message.clone(),
144144
})
145145
} else {
146-
MappedRustChildDiagnostic::SuggestedFix(CodeAction {
146+
MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147147
title: rd.message.clone(),
148148
kind: Some("quickfix".to_string()),
149-
diagnostics: None,
150-
edit: Some(WorkspaceEdit::new(edit_map)),
149+
edit: Some(lsp_ext::SnippetWorkspaceEdit {
150+
// FIXME: there's no good reason to use edit_map here....
151+
changes: Some(edit_map),
152+
document_changes: None,
153+
}),
151154
command: None,
152-
is_preferred: None,
153155
})
154156
}
155157
}
@@ -158,7 +160,7 @@ fn map_rust_child_diagnostic(
158160
pub(crate) struct MappedRustDiagnostic {
159161
pub location: Location,
160162
pub diagnostic: Diagnostic,
161-
pub fixes: Vec<CodeAction>,
163+
pub fixes: Vec<lsp_ext::CodeAction>,
162164
}
163165

164166
/// Converts a Rust root diagnostic to LSP form

crates/rust-analyzer/src/lsp_ext.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! rust-analyzer extensions to the LSP.
22
3-
use std::path::PathBuf;
3+
use std::{collections::HashMap, path::PathBuf};
44

55
use lsp_types::request::Request;
66
use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
@@ -137,7 +137,7 @@ pub struct Runnable {
137137
#[serde(rename_all = "camelCase")]
138138
pub struct SourceChange {
139139
pub label: String,
140-
pub workspace_edit: lsp_types::WorkspaceEdit,
140+
pub workspace_edit: SnippetWorkspaceEdit,
141141
pub cursor_position: Option<lsp_types::TextDocumentPositionParams>,
142142
}
143143

@@ -183,3 +183,52 @@ pub struct SsrParams {
183183
pub query: String,
184184
pub parse_only: bool,
185185
}
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+
}

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,14 +518,14 @@ fn on_request(
518518
.on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
519519
.on::<lsp_ext::Runnables>(handlers::handle_runnables)?
520520
.on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
521+
.on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
521522
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
522523
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
523524
.on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
524525
.on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)?
525526
.on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
526527
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
527528
.on::<lsp_types::request::Completion>(handlers::handle_completion)?
528-
.on::<lsp_types::request::CodeActionRequest>(handlers::handle_code_action)?
529529
.on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
530530
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
531531
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?

crates/rust-analyzer/src/main_loop/handlers.rs

Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ use lsp_server::ErrorCode;
1111
use lsp_types::{
1212
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
1313
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14-
CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
15-
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
16-
Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
17-
Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
18-
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
19-
TextEdit, Url, WorkspaceEdit,
14+
CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
15+
DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
16+
MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
17+
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18+
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit,
2019
};
2120
use ra_ide::{
2221
Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
@@ -585,9 +584,8 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
585584
None => return Ok(None),
586585
Some(it) => it.info,
587586
};
588-
589-
let source_change = to_proto::source_change(&world, source_change)?;
590-
Ok(Some(source_change.workspace_edit))
587+
let workspace_edit = to_proto::workspace_edit(&world, source_change)?;
588+
Ok(Some(workspace_edit))
591589
}
592590

593591
pub fn handle_references(
@@ -696,14 +694,21 @@ pub fn handle_formatting(
696694
pub fn handle_code_action(
697695
world: WorldSnapshot,
698696
params: lsp_types::CodeActionParams,
699-
) -> Result<Option<CodeActionResponse>> {
697+
) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
700698
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+
701706
let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
702707
let line_index = world.analysis().file_line_index(file_id)?;
703708
let range = from_proto::text_range(&line_index, params.range);
704709

705710
let diagnostics = world.analysis().diagnostics(file_id)?;
706-
let mut res = CodeActionResponse::default();
711+
let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
707712

708713
let fixes_from_diagnostics = diagnostics
709714
.into_iter()
@@ -713,22 +718,9 @@ pub fn handle_code_action(
713718

714719
for source_edit in fixes_from_diagnostics {
715720
let title = source_edit.label.clone();
716-
let edit = to_proto::source_change(&world, source_edit)?;
717-
718-
let command = Command {
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());
721+
let edit = to_proto::snippet_workspace_edit(&world, source_edit)?;
722+
let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None };
723+
res.push(action);
732724
}
733725

734726
for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
@@ -748,8 +740,13 @@ pub fn handle_code_action(
748740
.entry(label.to_owned())
749741
.or_insert_with(|| {
750742
let idx = res.len();
751-
let dummy = Command::new(String::new(), String::new(), None);
752-
res.push(dummy.into());
743+
let dummy = lsp_ext::CodeAction {
744+
title: String::new(),
745+
kind: None,
746+
command: None,
747+
edit: None,
748+
};
749+
res.push(dummy);
753750
(idx, Vec::new())
754751
})
755752
.1
@@ -777,35 +774,10 @@ pub fn handle_code_action(
777774
command: "rust-analyzer.selectAndApplySourceChange".to_string(),
778775
arguments: Some(vec![serde_json::Value::Array(arguments)]),
779776
});
780-
res[idx] = CodeAction {
781-
title,
782-
kind: None,
783-
diagnostics: None,
784-
edit: None,
785-
command,
786-
is_preferred: None,
787-
}
788-
.into();
777+
res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command };
789778
}
790779
}
791780

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-
}
809781
Ok(Some(res))
810782
}
811783

0 commit comments

Comments
 (0)