project: Implement range formatting for remoting (#47194)
Closes https://github.com/zed-industries/zed/issues/42859 Release Notes: - Added support for language range formatting in remoting setups
This commit is contained in:
1 parent
0abb39777d
commit
8e493cb434
3 files changed
+672
-555
No files matched your search
@@ -11,7 +11,7 @@ use assistant_text_thread::TextThreadStore;
|
||||
use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus, assert_hunks};
|
||||
use call::{ActiveCall, ParticipantLocation, Room, room};
|
||||
use client::{RECEIVE_TIMEOUT, User};
|
||||
use collections::{HashMap, HashSet};
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use fs::{FakeFs, Fs as _, RemoveOptions};
|
||||
use futures::{StreamExt as _, channel::mpsc};
|
||||
use git::{
|
||||
@@ -4641,6 +4641,97 @@ async fn test_formatting_buffer(
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_range_formatting_buffer(
|
||||
executor: BackgroundExecutor,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
let mut server = TestServer::start(executor.clone()).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
server
|
||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
client_a.language_registry().add(rust_lang());
|
||||
let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
document_range_formatting_provider: Some(OneOf::Left(true)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let directory = env::current_dir().unwrap();
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree(&directory, json!({ "a.rs": "one\ntwo\nthree\n" }))
|
||||
.await;
|
||||
let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
||||
|
||||
let buffer_b = project_b
|
||||
.update(cx_b, |p, cx| {
|
||||
p.open_buffer((worktree_id, rel_path("a.rs")), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _handle = project_b.update(cx_b, |project, cx| {
|
||||
project.register_buffer_with_language_servers(&buffer_b, cx)
|
||||
});
|
||||
let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
executor.run_until_parked();
|
||||
|
||||
fake_language_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
|
||||
|params, _| async move {
|
||||
assert_eq!(params.range.start, lsp::Position::new(0, 0));
|
||||
assert_eq!(params.range.end, lsp::Position::new(1, 3));
|
||||
Ok(Some(vec![lsp::TextEdit::new(
|
||||
lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
|
||||
", ".to_string(),
|
||||
)]))
|
||||
},
|
||||
);
|
||||
|
||||
let buffer_id = buffer_b.read_with(cx_b, |buffer, _| buffer.remote_id());
|
||||
let ranges = buffer_b.read_with(cx_b, |buffer, _| {
|
||||
let start = buffer.anchor_before(0);
|
||||
let end = buffer.anchor_after(7);
|
||||
vec![start..end]
|
||||
});
|
||||
|
||||
let mut ranges_map = BTreeMap::new();
|
||||
ranges_map.insert(buffer_id, ranges);
|
||||
|
||||
project_b
|
||||
.update(cx_b, |project, cx| {
|
||||
project.format(
|
||||
HashSet::from_iter([buffer_b.clone()]),
|
||||
LspFormatTarget::Ranges(ranges_map),
|
||||
true,
|
||||
FormatTrigger::Manual,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
|
||||
"one, two\nthree\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_prettier_formatting_buffer(
|
||||
executor: BackgroundExecutor,
|
||||
|
||||
@@ -69,8 +69,8 @@ use language::{
|
||||
language_settings::{FormatOnSave, Formatter, LanguageSettings, language_settings},
|
||||
point_to_lsp,
|
||||
proto::{
|
||||
deserialize_anchor, deserialize_lsp_edit, deserialize_version, serialize_anchor,
|
||||
serialize_lsp_edit, serialize_version,
|
||||
deserialize_anchor, deserialize_anchor_range, deserialize_lsp_edit, deserialize_version,
|
||||
serialize_anchor, serialize_anchor_range, serialize_lsp_edit, serialize_version,
|
||||
},
|
||||
range_from_lsp, range_to_lsp,
|
||||
row_chunk::RowChunk,
|
||||
@@ -10748,14 +10748,17 @@ impl LspStore {
|
||||
} else if let Some((client, project_id)) = self.upstream_client() {
|
||||
zlog::trace!(logger => "Formatting remotely");
|
||||
let logger = zlog::scoped!(logger => "remote");
|
||||
// Don't support formatting ranges via remote
|
||||
match target {
|
||||
LspFormatTarget::Buffers => {}
|
||||
LspFormatTarget::Ranges(_) => {
|
||||
zlog::trace!(logger => "Ignoring unsupported remote range formatting request");
|
||||
return Task::ready(Ok(ProjectTransaction::default()));
|
||||
}
|
||||
}
|
||||
|
||||
let buffer_ranges = match &target {
|
||||
LspFormatTarget::Buffers => Vec::new(),
|
||||
LspFormatTarget::Ranges(ranges) => ranges
|
||||
.iter()
|
||||
.map(|(buffer_id, ranges)| proto::BufferFormatRanges {
|
||||
buffer_id: buffer_id.to_proto(),
|
||||
ranges: ranges.iter().cloned().map(serialize_anchor_range).collect(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let buffer_store = self.buffer_store();
|
||||
cx.spawn(async move |lsp_store, cx| {
|
||||
@@ -10769,6 +10772,7 @@ impl LspStore {
|
||||
.iter()
|
||||
.map(|buffer| buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto()))
|
||||
.collect(),
|
||||
buffer_ranges,
|
||||
})
|
||||
.await
|
||||
.and_then(|result| result.transaction.context("missing transaction"));
|
||||
@@ -10810,8 +10814,27 @@ impl LspStore {
|
||||
let buffer_id = BufferId::new(*buffer_id)?;
|
||||
buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
|
||||
}
|
||||
|
||||
let target = if envelope.payload.buffer_ranges.is_empty() {
|
||||
LspFormatTarget::Buffers
|
||||
} else {
|
||||
let mut ranges_map = BTreeMap::new();
|
||||
for buffer_range in &envelope.payload.buffer_ranges {
|
||||
let buffer_id = BufferId::new(buffer_range.buffer_id)?;
|
||||
let ranges: Result<Vec<_>> = buffer_range
|
||||
.ranges
|
||||
.iter()
|
||||
.map(|range| {
|
||||
deserialize_anchor_range(range.clone()).context("invalid anchor range")
|
||||
})
|
||||
.collect();
|
||||
ranges_map.insert(buffer_id, ranges?);
|
||||
}
|
||||
LspFormatTarget::Ranges(ranges_map)
|
||||
};
|
||||
|
||||
let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
|
||||
anyhow::Ok(this.format(buffers, LspFormatTarget::Buffers, false, trigger, cx))
|
||||
anyhow::Ok(this.format(buffers, target, false, trigger, cx))
|
||||
})?;
|
||||
|
||||
let project_transaction = format.await?;
|
||||
|
||||
+546
-543
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user