internal: Allow OnTypeFormatting to send SnippetTextEdit

But continue to send TextEdit only.
This commit is contained in:
Felicián Németh 2022-03-27 10:45:57 +02:00
parent 3de03d4c61
commit 636d4880c4
5 changed files with 38 additions and 15 deletions

View file

@ -34,6 +34,11 @@ pub(crate) use on_enter::on_enter;
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
pub(crate) const TRIGGER_CHARS: &str = ".=>{"; pub(crate) const TRIGGER_CHARS: &str = ".=>{";
struct ExtendedTextEdit {
edit: TextEdit,
is_snippet: bool,
}
// Feature: On Typing Assists // Feature: On Typing Assists
// //
// Some features trigger on typing certain characters: // Some features trigger on typing certain characters:
@ -68,22 +73,27 @@ pub(crate) fn on_char_typed(
return None; return None;
} }
let edit = on_char_typed_inner(file, position.offset, char_typed)?; let edit = on_char_typed_inner(file, position.offset, char_typed)?;
Some(SourceChange::from_text_edit(position.file_id, edit)) let mut sc = SourceChange::from_text_edit(position.file_id, edit.edit);
sc.is_snippet = edit.is_snippet;
Some(sc)
} }
fn on_char_typed_inner( fn on_char_typed_inner(
file: &Parse<SourceFile>, file: &Parse<SourceFile>,
offset: TextSize, offset: TextSize,
char_typed: char, char_typed: char,
) -> Option<TextEdit> { ) -> Option<ExtendedTextEdit> {
fn conv(text_edit: Option<TextEdit>) -> Option<ExtendedTextEdit> {
Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
}
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
return None; return None;
} }
match char_typed { match char_typed {
'.' => on_dot_typed(&file.tree(), offset), '.' => conv(on_dot_typed(&file.tree(), offset)),
'=' => on_eq_typed(&file.tree(), offset), '=' => conv(on_eq_typed(&file.tree(), offset)),
'>' => on_arrow_typed(&file.tree(), offset), '>' => conv(on_arrow_typed(&file.tree(), offset)),
'{' => on_opening_brace_typed(file, offset), '{' => conv(on_opening_brace_typed(file, offset)),
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -276,7 +276,7 @@ pub(crate) fn handle_on_enter(
pub(crate) fn handle_on_type_formatting( pub(crate) fn handle_on_type_formatting(
snap: GlobalStateSnapshot, snap: GlobalStateSnapshot,
params: lsp_types::DocumentOnTypeFormattingParams, params: lsp_types::DocumentOnTypeFormattingParams,
) -> Result<Option<Vec<lsp_types::TextEdit>>> { ) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
let _p = profile::span("handle_on_type_formatting"); let _p = profile::span("handle_on_type_formatting");
let mut position = from_proto::file_position(&snap, params.text_document_position)?; let mut position = from_proto::file_position(&snap, params.text_document_position)?;
let line_index = snap.file_line_index(position.file_id)?; let line_index = snap.file_line_index(position.file_id)?;
@ -306,9 +306,9 @@ pub(crate) fn handle_on_type_formatting(
}; };
// This should be a single-file edit // This should be a single-file edit
let (_, edit) = edit.source_file_edits.into_iter().next().unwrap(); let (_, text_edit) = edit.source_file_edits.into_iter().next().unwrap();
let change = to_proto::text_edit_vec(&line_index, edit); let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit);
Ok(Some(change)) Ok(Some(change))
} }

View file

@ -4,8 +4,8 @@ use std::{collections::HashMap, path::PathBuf};
use lsp_types::request::Request; use lsp_types::request::Request;
use lsp_types::{ use lsp_types::{
notification::Notification, CodeActionKind, PartialResultParams, Position, Range, notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
TextDocumentIdentifier, WorkDoneProgressParams, PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -512,6 +512,19 @@ pub enum WorkspaceSymbolSearchKind {
AllSymbols, AllSymbols,
} }
/// The document on type formatting request is sent from the client to
/// the server to format parts of the document during typing. This is
/// almost same as lsp_types::request::OnTypeFormatting, but the
/// result has SnippetTextEdit in it instead of TextEdit.
#[derive(Debug)]
pub enum OnTypeFormatting {}
impl Request for OnTypeFormatting {
type Params = DocumentOnTypeFormattingParams;
type Result = Option<Vec<SnippetTextEdit>>;
const METHOD: &'static str = "textDocument/onTypeFormatting";
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct CompletionResolveData { pub struct CompletionResolveData {
pub position: lsp_types::TextDocumentPositionParams, pub position: lsp_types::TextDocumentPositionParams,

View file

@ -605,7 +605,7 @@ impl GlobalState {
.on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml) .on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
.on::<lsp_ext::MoveItem>(handlers::handle_move_item) .on::<lsp_ext::MoveItem>(handlers::handle_move_item)
.on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol) .on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting) .on::<lsp_ext::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::GotoDefinition>(handlers::handle_goto_definition) .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
.on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration) .on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)

View file

@ -1,5 +1,5 @@
<!--- <!---
lsp_ext.rs hash: 44e8238e4fbd4128 lsp_ext.rs hash: 2a188defec26cc7c
If you need to change the above hash to make the test pass, please check if you If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue: need to adjust this doc as well and ping this issue:
@ -47,7 +47,7 @@ If a language client does not know about `rust-analyzer`'s configuration options
**Experimental Client Capability:** `{ "snippetTextEdit": boolean }` **Experimental Client Capability:** `{ "snippetTextEdit": boolean }`
If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests and `TextEdit`s returned from `textDocument/onTypeFormatting` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s:
```typescript ```typescript
interface SnippetTextEdit extends TextEdit { interface SnippetTextEdit extends TextEdit {
@ -63,7 +63,7 @@ export interface TextDocumentEdit {
} }
``` ```
When applying such code action, the editor should insert snippet, with tab stops and placeholder. When applying such code action or text edit, the editor should insert snippet, with tab stops and placeholder.
At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`.
### Example ### Example