Make all formatting to happen on the client's buffers, as needed

This commit is contained in:
Kirill Bulatov 2023-10-12 15:26:47 +03:00
parent 1bfde4bfa2
commit 12d7d8db0a
5 changed files with 14 additions and 300 deletions

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic, diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic,
Diff, Language, Language,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use clock::ReplicaId; use clock::ReplicaId;
@ -587,42 +587,3 @@ pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry
}) })
.collect() .collect()
} }
pub fn serialize_diff(diff: Diff) -> proto::Diff {
proto::Diff {
version: serialize_version(&diff.base_version),
line_ending: serialize_line_ending(diff.line_ending) as i32,
edits: diff
.edits
.into_iter()
.map(|(range, edit)| proto::DiffEdit {
range: Some(proto::Range {
start: range.start as u64,
end: range.end as u64,
}),
edit: edit.to_string(),
})
.collect(),
}
}
pub fn deserialize_diff(diff: proto::Diff) -> Diff {
Diff {
base_version: deserialize_version(&diff.version),
line_ending: deserialize_line_ending(
rpc::proto::LineEnding::from_i32(diff.line_ending)
.unwrap_or_else(|| panic!("invalid line ending {}", diff.line_ending)),
),
edits: diff
.edits
.into_iter()
.map(|edit| {
let range = edit.range.expect("incorrect edit without a range");
(
range.start as usize..range.end as usize,
Arc::from(edit.edit.as_str()),
)
})
.collect(),
}
}

View File

@ -3,40 +3,29 @@ use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use anyhow::Context; use anyhow::Context;
use client::{proto, Client};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use fs::Fs; use fs::Fs;
use gpui::{AsyncAppContext, ModelHandle}; use gpui::{AsyncAppContext, ModelHandle};
use language::language_settings::language_settings; use language::language_settings::language_settings;
use language::proto::deserialize_diff;
use language::{Buffer, BundledFormatter, Diff}; use language::{Buffer, BundledFormatter, Diff};
use lsp::request::Request;
use lsp::{LanguageServer, LanguageServerId}; use lsp::{LanguageServer, LanguageServerId};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use util::paths::DEFAULT_PRETTIER_DIR; use util::paths::DEFAULT_PRETTIER_DIR;
pub enum Prettier { pub enum Prettier {
Local(Local), Real(RealPrettier),
Remote(Remote),
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Test(TestPrettier), Test(TestPrettier),
} }
pub struct Local { pub struct RealPrettier {
worktree_id: Option<usize>, worktree_id: Option<usize>,
default: bool, default: bool,
prettier_dir: PathBuf, prettier_dir: PathBuf,
server: Arc<LanguageServer>, server: Arc<LanguageServer>,
} }
pub struct Remote {
project_id: u64,
worktree_id: Option<usize>,
prettier_dir: PathBuf,
client: Arc<Client>,
}
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub struct TestPrettier { pub struct TestPrettier {
worktree_id: Option<usize>, worktree_id: Option<usize>,
@ -74,20 +63,6 @@ impl Prettier {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub const FORMAT_SUFFIX: &str = "\nformatted by test prettier"; pub const FORMAT_SUFFIX: &str = "\nformatted by test prettier";
pub fn remote(
project_id: u64,
worktree_id: Option<usize>,
prettier_dir: PathBuf,
client: Arc<Client>,
) -> Self {
Self::Remote(Remote {
project_id,
worktree_id,
prettier_dir,
client,
})
}
pub async fn locate( pub async fn locate(
starting_path: Option<LocateStart>, starting_path: Option<LocateStart>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
@ -249,7 +224,7 @@ impl Prettier {
.spawn(server.initialize(None)) .spawn(server.initialize(None))
.await .await
.context("prettier server initialization")?; .context("prettier server initialization")?;
Ok(Self::Local(Local { Ok(Self::Real(RealPrettier {
worktree_id, worktree_id,
server, server,
default: prettier_dir == DEFAULT_PRETTIER_DIR.as_path(), default: prettier_dir == DEFAULT_PRETTIER_DIR.as_path(),
@ -257,31 +232,6 @@ impl Prettier {
})) }))
} }
pub async fn invoke(
&self,
buffer: Option<&ModelHandle<Buffer>>,
buffer_path: Option<PathBuf>,
method: &str,
cx: &AsyncAppContext,
) -> anyhow::Result<Option<Diff>> {
match method {
Format::METHOD => self
.format(
buffer.expect("missing buffer for format invocation"),
buffer_path,
cx,
)
.await
.context("invoke method")
.map(Some),
ClearCache::METHOD => {
self.clear_cache().await.context("invoke method")?;
Ok(None)
}
unknown => anyhow::bail!("Unknown method {unknown}"),
}
}
pub async fn format( pub async fn format(
&self, &self,
buffer: &ModelHandle<Buffer>, buffer: &ModelHandle<Buffer>,
@ -289,7 +239,7 @@ impl Prettier {
cx: &AsyncAppContext, cx: &AsyncAppContext,
) -> anyhow::Result<Diff> { ) -> anyhow::Result<Diff> {
match self { match self {
Self::Local(local) => { Self::Real(local) => {
let params = buffer.read_with(cx, |buffer, cx| { let params = buffer.read_with(cx, |buffer, cx| {
let buffer_language = buffer.language(); let buffer_language = buffer.language();
let parsers_with_plugins = buffer_language let parsers_with_plugins = buffer_language
@ -418,21 +368,6 @@ impl Prettier {
let diff_task = buffer.read_with(cx, |buffer, cx| buffer.diff(response.text, cx)); let diff_task = buffer.read_with(cx, |buffer, cx| buffer.diff(response.text, cx));
Ok(diff_task.await) Ok(diff_task.await)
} }
Self::Remote(remote) => buffer
.read_with(cx, |buffer, _| {
remote.client.request(proto::InvokePrettierForBuffer {
buffer_id: Some(buffer.remote_id()),
worktree_id: self.worktree_id().map(|id| id as u64),
method: Format::METHOD.to_string(),
project_id: remote.project_id,
prettier_path: remote.prettier_dir.to_string_lossy().to_string(),
})
})
.await
.context("prettier diff invoke")?
.diff
.map(deserialize_diff)
.context("missing diff after prettier diff invocation"),
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Self::Test(_) => Ok(buffer Self::Test(_) => Ok(buffer
.read_with(cx, |buffer, cx| { .read_with(cx, |buffer, cx| {
@ -445,28 +380,11 @@ impl Prettier {
pub async fn clear_cache(&self) -> anyhow::Result<()> { pub async fn clear_cache(&self) -> anyhow::Result<()> {
match self { match self {
Self::Local(local) => local Self::Real(local) => local
.server .server
.request::<ClearCache>(()) .request::<ClearCache>(())
.await .await
.context("prettier clear cache"), .context("prettier clear cache"),
Self::Remote(remote) => remote
.client
.request(proto::InvokePrettierForBuffer {
buffer_id: None,
worktree_id: self.worktree_id().map(|id| id as u64),
method: ClearCache::METHOD.to_string(),
project_id: remote.project_id,
prettier_path: remote.prettier_dir.to_string_lossy().to_string(),
})
.await
.map(|response| {
debug_assert!(
response.diff.is_none(),
"Cleare cache invocation returned diff data"
)
})
.context("prettier invoke clear cache"),
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Self::Test(_) => Ok(()), Self::Test(_) => Ok(()),
} }
@ -474,8 +392,7 @@ impl Prettier {
pub fn server(&self) -> Option<&Arc<LanguageServer>> { pub fn server(&self) -> Option<&Arc<LanguageServer>> {
match self { match self {
Self::Local(local) => Some(&local.server), Self::Real(local) => Some(&local.server),
Self::Remote(_) => None,
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Self::Test(_) => None, Self::Test(_) => None,
} }
@ -483,8 +400,7 @@ impl Prettier {
pub fn is_default(&self) -> bool { pub fn is_default(&self) -> bool {
match self { match self {
Self::Local(local) => local.default, Self::Real(local) => local.default,
Self::Remote(_) => false,
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Self::Test(test_prettier) => test_prettier.default, Self::Test(test_prettier) => test_prettier.default,
} }
@ -492,8 +408,7 @@ impl Prettier {
pub fn prettier_dir(&self) -> &Path { pub fn prettier_dir(&self) -> &Path {
match self { match self {
Self::Local(local) => &local.prettier_dir, Self::Real(local) => &local.prettier_dir,
Self::Remote(remote) => &remote.prettier_dir,
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Self::Test(test_prettier) => &test_prettier.prettier_dir, Self::Test(test_prettier) => &test_prettier.prettier_dir,
} }
@ -501,8 +416,7 @@ impl Prettier {
pub fn worktree_id(&self) -> Option<usize> { pub fn worktree_id(&self) -> Option<usize> {
match self { match self {
Self::Local(local) => local.worktree_id, Self::Real(local) => local.worktree_id,
Self::Remote(remote) => remote.worktree_id,
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
Self::Test(test_prettier) => test_prettier.worktree_id, Self::Test(test_prettier) => test_prettier.worktree_id,
} }

View File

@ -37,7 +37,7 @@ use language::{
point_to_lsp, point_to_lsp,
proto::{ proto::{
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
serialize_anchor, serialize_diff, serialize_version, split_operations, serialize_anchor, serialize_version, split_operations,
}, },
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, BundledFormatter, CachedLspAdapter, range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, BundledFormatter, CachedLspAdapter,
CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
@ -613,8 +613,6 @@ impl Project {
client.add_model_request_handler(Self::handle_open_buffer_by_path); client.add_model_request_handler(Self::handle_open_buffer_by_path);
client.add_model_request_handler(Self::handle_save_buffer); client.add_model_request_handler(Self::handle_save_buffer);
client.add_model_message_handler(Self::handle_update_diff_base); client.add_model_message_handler(Self::handle_update_diff_base);
client.add_model_request_handler(Self::handle_prettier_instance_for_buffer);
client.add_model_request_handler(Self::handle_invoke_prettier);
} }
pub fn local( pub fn local(
@ -8310,84 +8308,6 @@ impl Project {
} }
} }
async fn handle_prettier_instance_for_buffer(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::PrettierInstanceForBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> anyhow::Result<proto::PrettierInstanceForBufferResponse> {
let prettier_instance_for_buffer_task = this.update(&mut cx, |this, cx| {
let buffer = this
.opened_buffers
.get(&envelope.payload.buffer_id)
.and_then(|buffer| buffer.upgrade(cx))
.with_context(|| format!("unknown buffer id {}", envelope.payload.buffer_id))?;
anyhow::Ok(this.prettier_instance_for_buffer(&buffer, cx))
})?;
let prettier_path = match prettier_instance_for_buffer_task.await {
Some(prettier) => match prettier.await {
Ok(prettier) => Some(prettier.prettier_dir().display().to_string()),
Err(e) => {
anyhow::bail!("Failed to create prettier instance for remote request: {e:#}")
}
},
None => None,
};
Ok(proto::PrettierInstanceForBufferResponse { prettier_path })
}
async fn handle_invoke_prettier(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::InvokePrettierForBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> anyhow::Result<proto::InvokePrettierForBufferResponse> {
let prettier = this
.read_with(&cx, |this, _| {
this.prettier_instances
.get(&(
envelope.payload.worktree_id.map(WorktreeId::from_proto),
PathBuf::from(&envelope.payload.prettier_path),
))
.cloned()
})
.with_context(|| {
format!(
"Missing prettier for worktree {:?} and path {:?}",
envelope.payload.worktree_id, envelope.payload.prettier_path,
)
})?
.await;
let prettier = match prettier {
Ok(prettier) => prettier,
Err(e) => anyhow::bail!("Prettier instance failed to start: {e:#}"),
};
let buffer = this.update(&mut cx, |this, cx| {
envelope
.payload
.buffer_id
.and_then(|id| this.opened_buffers.get(&id))
.and_then(|buffer| buffer.upgrade(cx))
});
let buffer_path = buffer.as_ref().and_then(|buffer| {
buffer.read_with(&cx, |buffer, cx| {
File::from_dyn(buffer.file()).map(|f| f.full_path(cx))
})
});
let diff = prettier
.invoke(buffer.as_ref(), buffer_path, &envelope.payload.method, &cx)
.await
.with_context(|| format!("prettier invoke method {}", &envelope.payload.method))?;
Ok(proto::InvokePrettierForBufferResponse {
diff: diff.map(serialize_diff),
})
}
fn prettier_instance_for_buffer( fn prettier_instance_for_buffer(
&mut self, &mut self,
buffer: &ModelHandle<Buffer>, buffer: &ModelHandle<Buffer>,
@ -8531,44 +8451,8 @@ impl Project {
}); });
Some(new_prettier_task) Some(new_prettier_task)
}) })
} else if let Some(project_id) = self.remote_id() { } else if self.remote_id().is_some() {
let client = self.client.clone(); return Task::ready(None);
let request = proto::PrettierInstanceForBuffer {
project_id,
buffer_id: buffer.remote_id(),
};
cx.spawn(|this, mut cx| async move {
match client.request(request).await {
Ok(response) => {
response
.prettier_path
.map(PathBuf::from)
.map(|prettier_path| {
let prettier_task = Task::ready(
Ok(Arc::new(Prettier::remote(
project_id,
worktree_id.map(|id| id.to_usize()),
prettier_path.clone(),
client,
)))
.map_err(Arc::new),
)
.shared();
this.update(&mut cx, |project, _| {
project.prettier_instances.insert(
(worktree_id, prettier_path),
prettier_task.clone(),
);
});
prettier_task
})
}
Err(e) => {
log::error!("Prettier init remote request failed: {e:#}");
None
}
}
})
} else { } else {
Task::ready(Some( Task::ready(Some(
Task::ready(Err(Arc::new(anyhow!("project does not have a remote id")))).shared(), Task::ready(Err(Arc::new(anyhow!("project does not have a remote id")))).shared(),

View File

@ -170,12 +170,7 @@ message Envelope {
LinkChannel link_channel = 140; LinkChannel link_channel = 140;
UnlinkChannel unlink_channel = 141; UnlinkChannel unlink_channel = 141;
MoveChannel move_channel = 142; MoveChannel move_channel = 142; // Current max: 144
PrettierInstanceForBuffer prettier_instance_for_buffer = 145;
PrettierInstanceForBufferResponse prettier_instance_for_buffer_response = 146;
InvokePrettierForBuffer invoke_prettier_for_buffer = 147;
InvokePrettierForBufferResponse invoke_prettier_for_buffer_response = 148; // Current max: 148
} }
} }
@ -1562,35 +1557,3 @@ message UpdateDiffBase {
uint64 buffer_id = 2; uint64 buffer_id = 2;
optional string diff_base = 3; optional string diff_base = 3;
} }
message PrettierInstanceForBuffer {
uint64 project_id = 1;
uint64 buffer_id = 2;
}
message PrettierInstanceForBufferResponse {
optional string prettier_path = 1;
}
message InvokePrettierForBuffer {
uint64 project_id = 1;
string prettier_path = 2;
optional uint64 buffer_id = 3;
optional uint64 worktree_id = 4;
string method = 5;
}
message InvokePrettierForBufferResponse {
optional Diff diff = 1;
}
message Diff {
repeated VectorClockEntry version = 1;
LineEnding line_ending = 2;
repeated DiffEdit edits = 3;
}
message DiffEdit {
Range range = 1;
string edit = 2;
}

View File

@ -273,10 +273,6 @@ messages!(
(UpdateChannelBufferCollaborators, Foreground), (UpdateChannelBufferCollaborators, Foreground),
(AckBufferOperation, Background), (AckBufferOperation, Background),
(AckChannelMessage, Background), (AckChannelMessage, Background),
(PrettierInstanceForBuffer, Background),
(InvokePrettierForBuffer, Background),
(PrettierInstanceForBufferResponse, Background),
(InvokePrettierForBufferResponse, Background),
); );
request_messages!( request_messages!(
@ -354,8 +350,6 @@ request_messages!(
(UpdateWorktree, Ack), (UpdateWorktree, Ack),
(JoinChannelBuffer, JoinChannelBufferResponse), (JoinChannelBuffer, JoinChannelBufferResponse),
(LeaveChannelBuffer, Ack), (LeaveChannelBuffer, Ack),
(PrettierInstanceForBuffer, PrettierInstanceForBufferResponse),
(InvokePrettierForBuffer, InvokePrettierForBufferResponse),
); );
entity_messages!( entity_messages!(
@ -407,8 +401,6 @@ entity_messages!(
UpdateWorktree, UpdateWorktree,
UpdateWorktreeSettings, UpdateWorktreeSettings,
UpdateDiffBase, UpdateDiffBase,
PrettierInstanceForBuffer,
InvokePrettierForBuffer,
); );
entity_messages!( entity_messages!(