mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
Server side of SnippetTextEdit
This commit is contained in:
parent
a752853350
commit
2bf6b16a7f
9 changed files with 200 additions and 119 deletions
|
@ -208,16 +208,6 @@ impl AssistBuilder {
|
||||||
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
|
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
|
||||||
self.edit.replace(range, replace_with.into())
|
self.edit.replace(range, replace_with.into())
|
||||||
}
|
}
|
||||||
/// Append specified `text` at the given `offset`
|
|
||||||
pub(crate) fn replace_snippet(
|
|
||||||
&mut self,
|
|
||||||
_cap: SnippetCap,
|
|
||||||
range: TextRange,
|
|
||||||
replace_with: impl Into<String>,
|
|
||||||
) {
|
|
||||||
self.is_snippet = true;
|
|
||||||
self.edit.replace(range, replace_with.into())
|
|
||||||
}
|
|
||||||
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
|
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
|
||||||
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
|
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ pub(crate) mod to_proto;
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use lsp_types::{CodeActionOrCommand, Diagnostic, Range};
|
use lsp_types::{Diagnostic, Range};
|
||||||
use ra_ide::FileId;
|
use ra_ide::FileId;
|
||||||
|
|
||||||
|
use crate::lsp_ext;
|
||||||
|
|
||||||
pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
|
pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
@ -18,13 +20,13 @@ pub struct DiagnosticCollection {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Fix {
|
pub struct Fix {
|
||||||
pub range: Range,
|
pub range: Range,
|
||||||
pub action: CodeActionOrCommand,
|
pub action: lsp_ext::CodeAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DiagnosticTask {
|
pub enum DiagnosticTask {
|
||||||
ClearCheck,
|
ClearCheck,
|
||||||
AddCheck(FileId, Diagnostic, Vec<CodeActionOrCommand>),
|
AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
|
||||||
SetNative(FileId, Vec<Diagnostic>),
|
SetNative(FileId, Vec<Diagnostic>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ impl DiagnosticCollection {
|
||||||
&mut self,
|
&mut self,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
diagnostic: Diagnostic,
|
diagnostic: Diagnostic,
|
||||||
fixes: Vec<CodeActionOrCommand>,
|
fixes: Vec<lsp_ext::CodeAction>,
|
||||||
) {
|
) {
|
||||||
let diagnostics = self.check.entry(file_id).or_default();
|
let diagnostics = self.check.entry(file_id).or_default();
|
||||||
for existing_diagnostic in diagnostics.iter() {
|
for existing_diagnostic in diagnostics.iter() {
|
||||||
|
|
|
@ -68,9 +68,9 @@ expression: diag
|
||||||
kind: Some(
|
kind: Some(
|
||||||
"quickfix",
|
"quickfix",
|
||||||
),
|
),
|
||||||
diagnostics: None,
|
command: None,
|
||||||
edit: Some(
|
edit: Some(
|
||||||
WorkspaceEdit {
|
SnippetWorkspaceEdit {
|
||||||
changes: Some(
|
changes: Some(
|
||||||
{
|
{
|
||||||
"file:///test/src/main.rs": [
|
"file:///test/src/main.rs": [
|
||||||
|
@ -106,8 +106,6 @@ expression: diag
|
||||||
document_changes: None,
|
document_changes: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
command: None,
|
|
||||||
is_preferred: None,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,9 +53,9 @@ expression: diag
|
||||||
kind: Some(
|
kind: Some(
|
||||||
"quickfix",
|
"quickfix",
|
||||||
),
|
),
|
||||||
diagnostics: None,
|
command: None,
|
||||||
edit: Some(
|
edit: Some(
|
||||||
WorkspaceEdit {
|
SnippetWorkspaceEdit {
|
||||||
changes: Some(
|
changes: Some(
|
||||||
{
|
{
|
||||||
"file:///test/driver/subcommand/repl.rs": [
|
"file:///test/driver/subcommand/repl.rs": [
|
||||||
|
@ -78,8 +78,6 @@ expression: diag
|
||||||
document_changes: None,
|
document_changes: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
command: None,
|
|
||||||
is_preferred: None,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,13 +7,13 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag,
|
Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
|
||||||
Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
|
NumberOrString, Position, Range, TextEdit, Url,
|
||||||
};
|
};
|
||||||
use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
|
use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
|
|
||||||
use crate::Result;
|
use crate::{lsp_ext, Result};
|
||||||
|
|
||||||
/// Converts a Rust level string to a LSP severity
|
/// Converts a Rust level string to a LSP severity
|
||||||
fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
|
fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
|
||||||
|
@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
|
||||||
|
|
||||||
enum MappedRustChildDiagnostic {
|
enum MappedRustChildDiagnostic {
|
||||||
Related(DiagnosticRelatedInformation),
|
Related(DiagnosticRelatedInformation),
|
||||||
SuggestedFix(CodeAction),
|
SuggestedFix(lsp_ext::CodeAction),
|
||||||
MessageLine(String),
|
MessageLine(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,13 +143,15 @@ fn map_rust_child_diagnostic(
|
||||||
message: rd.message.clone(),
|
message: rd.message.clone(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
MappedRustChildDiagnostic::SuggestedFix(CodeAction {
|
MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
|
||||||
title: rd.message.clone(),
|
title: rd.message.clone(),
|
||||||
kind: Some("quickfix".to_string()),
|
kind: Some("quickfix".to_string()),
|
||||||
diagnostics: None,
|
edit: Some(lsp_ext::SnippetWorkspaceEdit {
|
||||||
edit: Some(WorkspaceEdit::new(edit_map)),
|
// FIXME: there's no good reason to use edit_map here....
|
||||||
|
changes: Some(edit_map),
|
||||||
|
document_changes: None,
|
||||||
|
}),
|
||||||
command: None,
|
command: None,
|
||||||
is_preferred: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +160,7 @@ fn map_rust_child_diagnostic(
|
||||||
pub(crate) struct MappedRustDiagnostic {
|
pub(crate) struct MappedRustDiagnostic {
|
||||||
pub location: Location,
|
pub location: Location,
|
||||||
pub diagnostic: Diagnostic,
|
pub diagnostic: Diagnostic,
|
||||||
pub fixes: Vec<CodeAction>,
|
pub fixes: Vec<lsp_ext::CodeAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a Rust root diagnostic to LSP form
|
/// Converts a Rust root diagnostic to LSP form
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! rust-analyzer extensions to the LSP.
|
//! rust-analyzer extensions to the LSP.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use lsp_types::request::Request;
|
use lsp_types::request::Request;
|
||||||
use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
|
use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
|
||||||
|
@ -137,7 +137,7 @@ pub struct Runnable {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SourceChange {
|
pub struct SourceChange {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub workspace_edit: lsp_types::WorkspaceEdit,
|
pub workspace_edit: SnippetWorkspaceEdit,
|
||||||
pub cursor_position: Option<lsp_types::TextDocumentPositionParams>,
|
pub cursor_position: Option<lsp_types::TextDocumentPositionParams>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,3 +183,52 @@ pub struct SsrParams {
|
||||||
pub query: String,
|
pub query: String,
|
||||||
pub parse_only: bool,
|
pub parse_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum CodeActionRequest {}
|
||||||
|
|
||||||
|
impl Request for CodeActionRequest {
|
||||||
|
type Params = lsp_types::CodeActionParams;
|
||||||
|
type Result = Option<Vec<CodeAction>>;
|
||||||
|
const METHOD: &'static str = "textDocument/codeAction";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
|
||||||
|
pub struct CodeAction {
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub kind: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub command: Option<lsp_types::Command>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub edit: Option<SnippetWorkspaceEdit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SnippetWorkspaceEdit {
|
||||||
|
pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
|
||||||
|
pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged, rename_all = "lowercase")]
|
||||||
|
pub enum SnippetDocumentChangeOperation {
|
||||||
|
Op(lsp_types::ResourceOp),
|
||||||
|
Edit(SnippetTextDocumentEdit),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SnippetTextDocumentEdit {
|
||||||
|
pub text_document: lsp_types::VersionedTextDocumentIdentifier,
|
||||||
|
pub edits: Vec<SnippetTextEdit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SnippetTextEdit {
|
||||||
|
pub range: Range,
|
||||||
|
pub new_text: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub insert_text_format: Option<lsp_types::InsertTextFormat>,
|
||||||
|
}
|
||||||
|
|
|
@ -518,6 +518,7 @@ fn on_request(
|
||||||
.on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
|
.on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
|
||||||
.on::<lsp_ext::Runnables>(handlers::handle_runnables)?
|
.on::<lsp_ext::Runnables>(handlers::handle_runnables)?
|
||||||
.on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
|
.on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
|
||||||
|
.on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
|
||||||
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
|
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
|
||||||
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
|
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
|
||||||
.on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
|
.on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
|
||||||
|
@ -525,7 +526,6 @@ fn on_request(
|
||||||
.on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
|
.on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
|
||||||
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
|
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
|
||||||
.on::<lsp_types::request::Completion>(handlers::handle_completion)?
|
.on::<lsp_types::request::Completion>(handlers::handle_completion)?
|
||||||
.on::<lsp_types::request::CodeActionRequest>(handlers::handle_code_action)?
|
|
||||||
.on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
|
.on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
|
||||||
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
|
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
|
||||||
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
|
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
|
||||||
|
|
|
@ -11,12 +11,11 @@ use lsp_server::ErrorCode;
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
|
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
|
||||||
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
|
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
|
||||||
CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
|
CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
|
||||||
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
|
DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
|
||||||
Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
|
MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
|
||||||
Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
|
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
||||||
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
|
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit,
|
||||||
TextEdit, Url, WorkspaceEdit,
|
|
||||||
};
|
};
|
||||||
use ra_ide::{
|
use ra_ide::{
|
||||||
Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
||||||
|
@ -585,9 +584,8 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
Some(it) => it.info,
|
Some(it) => it.info,
|
||||||
};
|
};
|
||||||
|
let workspace_edit = to_proto::workspace_edit(&world, source_change)?;
|
||||||
let source_change = to_proto::source_change(&world, source_change)?;
|
Ok(Some(workspace_edit))
|
||||||
Ok(Some(source_change.workspace_edit))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_references(
|
pub fn handle_references(
|
||||||
|
@ -696,14 +694,21 @@ pub fn handle_formatting(
|
||||||
pub fn handle_code_action(
|
pub fn handle_code_action(
|
||||||
world: WorldSnapshot,
|
world: WorldSnapshot,
|
||||||
params: lsp_types::CodeActionParams,
|
params: lsp_types::CodeActionParams,
|
||||||
) -> Result<Option<CodeActionResponse>> {
|
) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
|
||||||
let _p = profile("handle_code_action");
|
let _p = profile("handle_code_action");
|
||||||
|
// We intentionally don't support command-based actions, as those either
|
||||||
|
// requires custom client-code anyway, or requires server-initiated edits.
|
||||||
|
// Server initiated edits break causality, so we avoid those as well.
|
||||||
|
if !world.config.client_caps.code_action_literals {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?;
|
let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?;
|
||||||
let line_index = world.analysis().file_line_index(file_id)?;
|
let line_index = world.analysis().file_line_index(file_id)?;
|
||||||
let range = from_proto::text_range(&line_index, params.range);
|
let range = from_proto::text_range(&line_index, params.range);
|
||||||
|
|
||||||
let diagnostics = world.analysis().diagnostics(file_id)?;
|
let diagnostics = world.analysis().diagnostics(file_id)?;
|
||||||
let mut res = CodeActionResponse::default();
|
let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
|
||||||
|
|
||||||
let fixes_from_diagnostics = diagnostics
|
let fixes_from_diagnostics = diagnostics
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -713,22 +718,9 @@ pub fn handle_code_action(
|
||||||
|
|
||||||
for source_edit in fixes_from_diagnostics {
|
for source_edit in fixes_from_diagnostics {
|
||||||
let title = source_edit.label.clone();
|
let title = source_edit.label.clone();
|
||||||
let edit = to_proto::source_change(&world, source_edit)?;
|
let edit = to_proto::snippet_workspace_edit(&world, source_edit)?;
|
||||||
|
let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None };
|
||||||
let command = Command {
|
res.push(action);
|
||||||
title,
|
|
||||||
command: "rust-analyzer.applySourceChange".to_string(),
|
|
||||||
arguments: Some(vec![to_value(edit).unwrap()]),
|
|
||||||
};
|
|
||||||
let action = CodeAction {
|
|
||||||
title: command.title.clone(),
|
|
||||||
kind: None,
|
|
||||||
diagnostics: None,
|
|
||||||
edit: None,
|
|
||||||
command: Some(command),
|
|
||||||
is_preferred: None,
|
|
||||||
};
|
|
||||||
res.push(action.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
|
for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
|
||||||
|
@ -748,8 +740,13 @@ pub fn handle_code_action(
|
||||||
.entry(label.to_owned())
|
.entry(label.to_owned())
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let idx = res.len();
|
let idx = res.len();
|
||||||
let dummy = Command::new(String::new(), String::new(), None);
|
let dummy = lsp_ext::CodeAction {
|
||||||
res.push(dummy.into());
|
title: String::new(),
|
||||||
|
kind: None,
|
||||||
|
command: None,
|
||||||
|
edit: None,
|
||||||
|
};
|
||||||
|
res.push(dummy);
|
||||||
(idx, Vec::new())
|
(idx, Vec::new())
|
||||||
})
|
})
|
||||||
.1
|
.1
|
||||||
|
@ -777,35 +774,10 @@ pub fn handle_code_action(
|
||||||
command: "rust-analyzer.selectAndApplySourceChange".to_string(),
|
command: "rust-analyzer.selectAndApplySourceChange".to_string(),
|
||||||
arguments: Some(vec![serde_json::Value::Array(arguments)]),
|
arguments: Some(vec![serde_json::Value::Array(arguments)]),
|
||||||
});
|
});
|
||||||
res[idx] = CodeAction {
|
res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command };
|
||||||
title,
|
|
||||||
kind: None,
|
|
||||||
diagnostics: None,
|
|
||||||
edit: None,
|
|
||||||
command,
|
|
||||||
is_preferred: None,
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the client only supports commands then filter the list
|
|
||||||
// and remove and actions that depend on edits.
|
|
||||||
if !world.config.client_caps.code_action_literals {
|
|
||||||
// FIXME: use drain_filter once it hits stable.
|
|
||||||
res = res
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|it| match it {
|
|
||||||
cmd @ lsp_types::CodeActionOrCommand::Command(_) => Some(cmd),
|
|
||||||
lsp_types::CodeActionOrCommand::CodeAction(action) => match action.command {
|
|
||||||
Some(cmd) if action.edit.is_none() => {
|
|
||||||
Some(lsp_types::CodeActionOrCommand::Command(cmd))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,22 @@ pub(crate) fn text_edit(
|
||||||
lsp_types::TextEdit { range, new_text }
|
lsp_types::TextEdit { range, new_text }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn snippet_text_edit(
|
||||||
|
line_index: &LineIndex,
|
||||||
|
line_endings: LineEndings,
|
||||||
|
is_snippet: bool,
|
||||||
|
indel: Indel,
|
||||||
|
) -> lsp_ext::SnippetTextEdit {
|
||||||
|
let text_edit = text_edit(line_index, line_endings, indel);
|
||||||
|
let insert_text_format =
|
||||||
|
if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None };
|
||||||
|
lsp_ext::SnippetTextEdit {
|
||||||
|
range: text_edit.range,
|
||||||
|
new_text: text_edit.new_text,
|
||||||
|
insert_text_format,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn text_edit_vec(
|
pub(crate) fn text_edit_vec(
|
||||||
line_index: &LineIndex,
|
line_index: &LineIndex,
|
||||||
line_endings: LineEndings,
|
line_endings: LineEndings,
|
||||||
|
@ -441,10 +457,11 @@ pub(crate) fn goto_definition_response(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn text_document_edit(
|
pub(crate) fn snippet_text_document_edit(
|
||||||
world: &WorldSnapshot,
|
world: &WorldSnapshot,
|
||||||
|
is_snippet: bool,
|
||||||
source_file_edit: SourceFileEdit,
|
source_file_edit: SourceFileEdit,
|
||||||
) -> Result<lsp_types::TextDocumentEdit> {
|
) -> Result<lsp_ext::SnippetTextDocumentEdit> {
|
||||||
let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?;
|
let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?;
|
||||||
let line_index = world.analysis().file_line_index(source_file_edit.file_id)?;
|
let line_index = world.analysis().file_line_index(source_file_edit.file_id)?;
|
||||||
let line_endings = world.file_line_endings(source_file_edit.file_id);
|
let line_endings = world.file_line_endings(source_file_edit.file_id);
|
||||||
|
@ -452,9 +469,9 @@ pub(crate) fn text_document_edit(
|
||||||
.edit
|
.edit
|
||||||
.as_indels()
|
.as_indels()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|it| text_edit(&line_index, line_endings, it.clone()))
|
.map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
Ok(lsp_types::TextDocumentEdit { text_document, edits })
|
Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resource_op(
|
pub(crate) fn resource_op(
|
||||||
|
@ -500,20 +517,70 @@ pub(crate) fn source_change(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut document_changes: Vec<lsp_types::DocumentChangeOperation> = Vec::new();
|
let label = source_change.label.clone();
|
||||||
|
let workspace_edit = self::snippet_workspace_edit(world, source_change)?;
|
||||||
|
Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn snippet_workspace_edit(
|
||||||
|
world: &WorldSnapshot,
|
||||||
|
source_change: SourceChange,
|
||||||
|
) -> Result<lsp_ext::SnippetWorkspaceEdit> {
|
||||||
|
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
|
||||||
for op in source_change.file_system_edits {
|
for op in source_change.file_system_edits {
|
||||||
let op = resource_op(&world, op)?;
|
let op = resource_op(&world, op)?;
|
||||||
document_changes.push(lsp_types::DocumentChangeOperation::Op(op));
|
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
|
||||||
}
|
}
|
||||||
for edit in source_change.source_file_edits {
|
for edit in source_change.source_file_edits {
|
||||||
let edit = text_document_edit(&world, edit)?;
|
let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?;
|
||||||
document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit));
|
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
|
||||||
|
}
|
||||||
|
let workspace_edit =
|
||||||
|
lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) };
|
||||||
|
Ok(workspace_edit)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn workspace_edit(
|
||||||
|
world: &WorldSnapshot,
|
||||||
|
source_change: SourceChange,
|
||||||
|
) -> Result<lsp_types::WorkspaceEdit> {
|
||||||
|
assert!(!source_change.is_snippet);
|
||||||
|
snippet_workspace_edit(world, source_change).map(|it| it.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
|
||||||
|
fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit {
|
||||||
|
lsp_types::WorkspaceEdit {
|
||||||
|
changes: None,
|
||||||
|
document_changes: snippet_workspace_edit.document_changes.map(|changes| {
|
||||||
|
lsp_types::DocumentChanges::Operations(
|
||||||
|
changes
|
||||||
|
.into_iter()
|
||||||
|
.map(|change| match change {
|
||||||
|
lsp_ext::SnippetDocumentChangeOperation::Op(op) => {
|
||||||
|
lsp_types::DocumentChangeOperation::Op(op)
|
||||||
|
}
|
||||||
|
lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => {
|
||||||
|
lsp_types::DocumentChangeOperation::Edit(
|
||||||
|
lsp_types::TextDocumentEdit {
|
||||||
|
text_document: edit.text_document,
|
||||||
|
edits: edit
|
||||||
|
.edits
|
||||||
|
.into_iter()
|
||||||
|
.map(|edit| lsp_types::TextEdit {
|
||||||
|
range: edit.range,
|
||||||
|
new_text: edit.new_text,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let workspace_edit = lsp_types::WorkspaceEdit {
|
|
||||||
changes: None,
|
|
||||||
document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)),
|
|
||||||
};
|
|
||||||
Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_hierarchy_item(
|
pub fn call_hierarchy_item(
|
||||||
|
@ -571,22 +638,25 @@ fn main() <fold>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> {
|
pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> {
|
||||||
let source_change = source_change(&world, assist.source_change)?;
|
let res = if assist.source_change.is_snippet {
|
||||||
let arg = serde_json::to_value(source_change)?;
|
lsp_ext::CodeAction {
|
||||||
let title = assist.label;
|
title: assist.label,
|
||||||
let command = lsp_types::Command {
|
kind: Some(String::new()),
|
||||||
title: title.clone(),
|
edit: Some(snippet_workspace_edit(world, assist.source_change)?),
|
||||||
command: "rust-analyzer.applySourceChange".to_string(),
|
command: None,
|
||||||
arguments: Some(vec![arg]),
|
}
|
||||||
};
|
} else {
|
||||||
|
let source_change = source_change(&world, assist.source_change)?;
|
||||||
|
let arg = serde_json::to_value(source_change)?;
|
||||||
|
let title = assist.label;
|
||||||
|
let command = lsp_types::Command {
|
||||||
|
title: title.clone(),
|
||||||
|
command: "rust-analyzer.applySourceChange".to_string(),
|
||||||
|
arguments: Some(vec![arg]),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(lsp_types::CodeAction {
|
lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) }
|
||||||
title,
|
};
|
||||||
kind: Some(String::new()),
|
Ok(res)
|
||||||
diagnostics: None,
|
|
||||||
edit: None,
|
|
||||||
command: Some(command),
|
|
||||||
is_preferred: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue