Merge pull request #1819 from zed-industries/remote-renames

Assign a new language when remote buffer is renamed
This commit is contained in:
Antonio Scandurra 2022-10-26 16:58:45 +01:00 committed by GitHub
commit cf2ec99a4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 61 deletions

View file

@ -34,8 +34,7 @@ use live_kit_client::MacOSDisplay;
use lsp::{self, FakeLanguageServer}; use lsp::{self, FakeLanguageServer};
use parking_lot::Mutex; use parking_lot::Mutex;
use project::{ use project::{
search::SearchQuery, worktree::WorktreeHandle, DiagnosticSummary, Project, ProjectPath, search::SearchQuery, DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId,
ProjectStore, WorktreeId,
}; };
use rand::prelude::*; use rand::prelude::*;
use serde_json::json; use serde_json::json;
@ -1228,26 +1227,49 @@ async fn test_room_location(
#[gpui::test(iterations = 10)] #[gpui::test(iterations = 10)]
async fn test_propagate_saves_and_fs_changes( async fn test_propagate_saves_and_fs_changes(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext, cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext, cx_b: &mut TestAppContext,
cx_c: &mut TestAppContext, cx_c: &mut TestAppContext,
) { ) {
cx_a.foreground().forbid_parking(); deterministic.forbid_parking();
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
let client_a = server.create_client(cx_a, "user_a").await; let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await; let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await; let client_c = server.create_client(cx_c, "user_c").await;
server server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
.await; .await;
let active_call_a = cx_a.read(ActiveCall::global); let active_call_a = cx_a.read(ActiveCall::global);
let rust = Arc::new(Language::new(
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
));
let javascript = Arc::new(Language::new(
LanguageConfig {
name: "JavaScript".into(),
path_suffixes: vec!["js".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
));
for client in [&client_a, &client_b, &client_c] {
client.language_registry.add(rust.clone());
client.language_registry.add(javascript.clone());
}
client_a client_a
.fs .fs
.insert_tree( .insert_tree(
"/a", "/a",
json!({ json!({
"file1": "", "file1.rs": "",
"file2": "" "file2": ""
}), }),
) )
@ -1267,19 +1289,25 @@ async fn test_propagate_saves_and_fs_changes(
// Open and edit a buffer as both guests B and C. // Open and edit a buffer as both guests B and C.
let buffer_b = project_b let buffer_b = project_b
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1"), cx)) .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
.await .await
.unwrap(); .unwrap();
let buffer_c = project_c let buffer_c = project_c
.update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1"), cx)) .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
.await .await
.unwrap(); .unwrap();
buffer_b.read_with(cx_b, |buffer, _| {
assert_eq!(&*buffer.language().unwrap().name(), "Rust");
});
buffer_c.read_with(cx_c, |buffer, _| {
assert_eq!(&*buffer.language().unwrap().name(), "Rust");
});
buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx)); buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx)); buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
// Open and edit that buffer as the host. // Open and edit that buffer as the host.
let buffer_a = project_a let buffer_a = project_a
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1"), cx)) .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1.rs"), cx))
.await .await
.unwrap(); .unwrap();
@ -1290,90 +1318,87 @@ async fn test_propagate_saves_and_fs_changes(
buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx) buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx)
}); });
// Wait for edits to propagate deterministic.run_until_parked();
buffer_a buffer_a.read_with(cx_a, |buf, _| {
.condition(cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a") assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
.await; });
buffer_b buffer_b.read_with(cx_b, |buf, _| {
.condition(cx_b, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a") assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
.await; });
buffer_c buffer_c.read_with(cx_c, |buf, _| {
.condition(cx_c, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a") assert_eq!(buf.text(), "i-am-c, i-am-b, i-am-a");
.await; });
// Edit the buffer as the host and concurrently save as guest B. // Edit the buffer as the host and concurrently save as guest B.
let save_b = buffer_b.update(cx_b, |buf, cx| buf.save(cx)); let save_b = buffer_b.update(cx_b, |buf, cx| buf.save(cx));
buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx)); buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx));
save_b.await.unwrap(); save_b.await.unwrap();
assert_eq!( assert_eq!(
client_a.fs.load("/a/file1".as_ref()).await.unwrap(), client_a.fs.load("/a/file1.rs".as_ref()).await.unwrap(),
"hi-a, i-am-c, i-am-b, i-am-a" "hi-a, i-am-c, i-am-b, i-am-a"
); );
deterministic.run_until_parked();
buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty())); buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty())); buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
buffer_c.condition(cx_c, |buf, _| !buf.is_dirty()).await; buffer_c.read_with(cx_c, |buf, _| assert!(!buf.is_dirty()));
worktree_a.flush_fs_events(cx_a).await;
// Make changes on host's file system, see those changes on guest worktrees. // Make changes on host's file system, see those changes on guest worktrees.
client_a client_a
.fs .fs
.rename( .rename(
"/a/file1".as_ref(), "/a/file1.rs".as_ref(),
"/a/file1-renamed".as_ref(), "/a/file1.js".as_ref(),
Default::default(), Default::default(),
) )
.await .await
.unwrap(); .unwrap();
client_a client_a
.fs .fs
.rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default()) .rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
.await .await
.unwrap(); .unwrap();
client_a.fs.insert_file("/a/file4", "4".into()).await; client_a.fs.insert_file("/a/file4", "4".into()).await;
deterministic.run_until_parked();
worktree_a worktree_a.read_with(cx_a, |tree, _| {
.condition(cx_a, |tree, _| { assert_eq!(
tree.paths() tree.paths()
.map(|p| p.to_string_lossy()) .map(|p| p.to_string_lossy())
.collect::<Vec<_>>() .collect::<Vec<_>>(),
== ["file1-renamed", "file3", "file4"] ["file1.js", "file3", "file4"]
}) )
.await; });
worktree_b worktree_b.read_with(cx_b, |tree, _| {
.condition(cx_b, |tree, _| { assert_eq!(
tree.paths() tree.paths()
.map(|p| p.to_string_lossy()) .map(|p| p.to_string_lossy())
.collect::<Vec<_>>() .collect::<Vec<_>>(),
== ["file1-renamed", "file3", "file4"] ["file1.js", "file3", "file4"]
}) )
.await; });
worktree_c worktree_c.read_with(cx_c, |tree, _| {
.condition(cx_c, |tree, _| { assert_eq!(
tree.paths() tree.paths()
.map(|p| p.to_string_lossy()) .map(|p| p.to_string_lossy())
.collect::<Vec<_>>() .collect::<Vec<_>>(),
== ["file1-renamed", "file3", "file4"] ["file1.js", "file3", "file4"]
}) )
.await; });
// Ensure buffer files are updated as well. // Ensure buffer files are updated as well.
buffer_a buffer_a.read_with(cx_a, |buffer, _| {
.condition(cx_a, |buf, _| { assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
buf.file().unwrap().path().to_str() == Some("file1-renamed") assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
}) });
.await; buffer_b.read_with(cx_b, |buffer, _| {
buffer_b assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
.condition(cx_b, |buf, _| { assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
buf.file().unwrap().path().to_str() == Some("file1-renamed") });
}) buffer_c.read_with(cx_c, |buffer, _| {
.await; assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
buffer_c assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
.condition(cx_c, |buf, _| { });
buf.file().unwrap().path().to_str() == Some("file1-renamed")
})
.await;
} }
#[gpui::test(iterations = 10)] #[gpui::test(iterations = 10)]

View file

@ -1780,17 +1780,21 @@ impl Project {
) -> Option<()> { ) -> Option<()> {
// If the buffer has a language, set it and start the language server if we haven't already. // If the buffer has a language, set it and start the language server if we haven't already.
let full_path = buffer.read(cx).file()?.full_path(cx); let full_path = buffer.read(cx).file()?.full_path(cx);
let language = self.languages.select_language(&full_path)?; let new_language = self.languages.select_language(&full_path)?;
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.set_language_registry(self.languages.clone()); if buffer.language().map_or(true, |old_language| {
buffer.set_language(Some(language.clone()), cx); !Arc::ptr_eq(old_language, &new_language)
}) {
buffer.set_language_registry(self.languages.clone());
buffer.set_language(Some(new_language.clone()), cx);
}
}); });
let file = File::from_dyn(buffer.read(cx).file())?; let file = File::from_dyn(buffer.read(cx).file())?;
let worktree = file.worktree.read(cx).as_local()?; let worktree = file.worktree.read(cx).as_local()?;
let worktree_id = worktree.id(); let worktree_id = worktree.id();
let worktree_abs_path = worktree.abs_path().clone(); let worktree_abs_path = worktree.abs_path().clone();
self.start_language_server(worktree_id, worktree_abs_path, language, cx); self.start_language_server(worktree_id, worktree_abs_path, new_language, cx);
None None
} }
@ -4992,6 +4996,7 @@ impl Project {
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.file_updated(Arc::new(file), cx).detach(); buffer.file_updated(Arc::new(file), cx).detach();
}); });
this.assign_language_to_buffer(&buffer, cx);
Ok(()) Ok(())
}) })
} }