mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-23 18:32:17 +00:00
Send and handle OnTypeFormatting LSP request
This commit is contained in:
parent
793486b2e8
commit
f6d7b3d2e8
3 changed files with 161 additions and 2 deletions
|
@ -2122,6 +2122,13 @@ impl Editor {
|
|||
let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
|
||||
|
||||
if text.len() == 1 {
|
||||
let input_char = text.chars().next().expect("single char input");
|
||||
if let Some(on_type_format_task) = this.trigger_on_type_format(input_char, cx) {
|
||||
on_type_format_task.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
|
||||
if had_active_copilot_suggestion {
|
||||
this.refresh_copilot_suggestions(true, cx);
|
||||
if !this.has_active_copilot_suggestion(cx) {
|
||||
|
@ -2500,6 +2507,23 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn trigger_on_type_format(
|
||||
&self,
|
||||
input: char,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
let project = self.project.as_ref()?;
|
||||
let position = self.selections.newest_anchor().head();
|
||||
let (buffer, buffer_position) = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.text_anchor_for_position(position.clone(), cx)?;
|
||||
|
||||
Some(project.update(cx, |project, cx| {
|
||||
project.on_type_format(buffer.clone(), buffer_position, input, cx)
|
||||
}))
|
||||
}
|
||||
|
||||
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
|
||||
if self.pending_rename.is_some() {
|
||||
return;
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
DocumentHighlight, Hover, HoverBlock, HoverBlockKind, Location, LocationLink, Project,
|
||||
ProjectTransaction,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::proto::{self, PeerId};
|
||||
use fs::LineEnding;
|
||||
|
@ -109,6 +109,12 @@ pub(crate) struct GetCodeActions {
|
|||
pub range: Range<Anchor>,
|
||||
}
|
||||
|
||||
pub(crate) struct OnTypeFormatting {
|
||||
pub position: PointUtf16,
|
||||
pub new_char: char,
|
||||
// TODO kb formatting options?
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for PrepareRename {
|
||||
type Response = Option<Range<Anchor>>;
|
||||
|
@ -1596,3 +1602,98 @@ impl LspCommand for GetCodeActions {
|
|||
message.buffer_id
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for OnTypeFormatting {
|
||||
type Response = Vec<(Range<Anchor>, String)>;
|
||||
type LspRequest = lsp::request::OnTypeFormatting;
|
||||
type ProtoRequest = proto::PerformRename;
|
||||
|
||||
fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
|
||||
let Some(on_type_formatting_options) = &server_capabilities.document_on_type_formatting_provider else { return false };
|
||||
on_type_formatting_options
|
||||
.first_trigger_character
|
||||
.contains(self.new_char)
|
||||
|| on_type_formatting_options
|
||||
.more_trigger_character
|
||||
.iter()
|
||||
.flatten()
|
||||
.any(|chars| chars.contains(self.new_char))
|
||||
}
|
||||
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::DocumentOnTypeFormattingParams {
|
||||
lsp::DocumentOnTypeFormattingParams {
|
||||
text_document_position: lsp::TextDocumentPositionParams::new(
|
||||
lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
|
||||
point_to_lsp(self.position),
|
||||
),
|
||||
ch: self.new_char.to_string(),
|
||||
// TODO kb pass current editor ones
|
||||
options: lsp::FormattingOptions::default(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_lsp(
|
||||
self,
|
||||
message: Option<Vec<lsp::TextEdit>>,
|
||||
project: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<(Range<Anchor>, String)>> {
|
||||
cx.update(|cx| {
|
||||
project.update(cx, |project, cx| {
|
||||
project.edits_from_lsp(&buffer, message.into_iter().flatten(), server_id, None, cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.context("LSP edits conversion")
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
|
||||
todo!("TODO kb")
|
||||
}
|
||||
|
||||
async fn from_proto(
|
||||
message: proto::PerformRename,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Self> {
|
||||
todo!("TODO kb")
|
||||
}
|
||||
|
||||
fn response_to_proto(
|
||||
response: Vec<(Range<Anchor>, String)>,
|
||||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
_: &clock::Global,
|
||||
cx: &mut AppContext,
|
||||
) -> proto::PerformRenameResponse {
|
||||
// let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
|
||||
// proto::PerformRenameResponse {
|
||||
// transaction: Some(transaction),
|
||||
// }
|
||||
todo!("TODO kb")
|
||||
}
|
||||
|
||||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::PerformRenameResponse,
|
||||
project: ModelHandle<Project>,
|
||||
_: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<(Range<Anchor>, String)>> {
|
||||
todo!("TODO kb")
|
||||
}
|
||||
|
||||
fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
|
||||
message.buffer_id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4209,6 +4209,40 @@ impl Project {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn on_type_format<T: ToPointUtf16>(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
position: T,
|
||||
input: char,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let position = position.to_point_utf16(buffer.read(cx));
|
||||
let edits_task = self.request_lsp(
|
||||
buffer.clone(),
|
||||
OnTypeFormatting {
|
||||
position,
|
||||
new_char: input,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.spawn(|_project, mut cx| async move {
|
||||
let edits = edits_task
|
||||
.await
|
||||
.context("requesting OnTypeFormatting edits for char '{new_char}'")?;
|
||||
|
||||
if !edits.is_empty() {
|
||||
cx.update(|cx| {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(edits, None, cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn search(
|
||||
&self,
|
||||
|
@ -6349,7 +6383,7 @@ impl Project {
|
|||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn edits_from_lsp(
|
||||
pub fn edits_from_lsp(
|
||||
&mut self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
lsp_edits: impl 'static + Send + IntoIterator<Item = lsp::TextEdit>,
|
||||
|
|
Loading…
Reference in a new issue