Fix renames over language server for SSH remoting (#17897)
Release Notes: - ssh remoting: Fix rename over language server --------- Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Max <max@zed.dev>
This commit is contained in:
parent
01bb10f518
commit
e66ea9e5d4
34 changed files with 505 additions and 329 deletions
|
@ -302,7 +302,7 @@ pub enum Operation {
|
|||
|
||||
/// An event that occurs in a buffer.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Event {
|
||||
pub enum BufferEvent {
|
||||
/// The buffer was changed in a way that must be
|
||||
/// propagated to its other replicas.
|
||||
Operation(Operation),
|
||||
|
@ -809,7 +809,7 @@ impl Buffer {
|
|||
self.syntax_map.lock().clear();
|
||||
self.language = language;
|
||||
self.reparse(cx);
|
||||
cx.emit(Event::LanguageChanged);
|
||||
cx.emit(BufferEvent::LanguageChanged);
|
||||
}
|
||||
|
||||
/// Assign a language registry to the buffer. This allows the buffer to retrieve
|
||||
|
@ -827,7 +827,7 @@ impl Buffer {
|
|||
/// Assign the buffer a new [Capability].
|
||||
pub fn set_capability(&mut self, capability: Capability, cx: &mut ModelContext<Self>) {
|
||||
self.capability = capability;
|
||||
cx.emit(Event::CapabilityChanged)
|
||||
cx.emit(BufferEvent::CapabilityChanged)
|
||||
}
|
||||
|
||||
/// This method is called to signal that the buffer has been saved.
|
||||
|
@ -842,13 +842,13 @@ impl Buffer {
|
|||
.set((self.saved_version().clone(), false));
|
||||
self.has_conflict = false;
|
||||
self.saved_mtime = mtime;
|
||||
cx.emit(Event::Saved);
|
||||
cx.emit(BufferEvent::Saved);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// This method is called to signal that the buffer has been discarded.
|
||||
pub fn discarded(&mut self, cx: &mut ModelContext<Self>) {
|
||||
cx.emit(Event::Discarded);
|
||||
cx.emit(BufferEvent::Discarded);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -911,7 +911,7 @@ impl Buffer {
|
|||
.set((self.saved_version.clone(), false));
|
||||
self.text.set_line_ending(line_ending);
|
||||
self.saved_mtime = mtime;
|
||||
cx.emit(Event::Reloaded);
|
||||
cx.emit(BufferEvent::Reloaded);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -929,7 +929,7 @@ impl Buffer {
|
|||
if !old_file.is_deleted() {
|
||||
file_changed = true;
|
||||
if !self.is_dirty() {
|
||||
cx.emit(Event::DirtyChanged);
|
||||
cx.emit(BufferEvent::DirtyChanged);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -949,7 +949,7 @@ impl Buffer {
|
|||
self.file = Some(new_file);
|
||||
if file_changed {
|
||||
self.non_text_state_update_count += 1;
|
||||
cx.emit(Event::FileHandleChanged);
|
||||
cx.emit(BufferEvent::FileHandleChanged);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
@ -974,7 +974,7 @@ impl Buffer {
|
|||
recalc_task.await;
|
||||
buffer
|
||||
.update(&mut cx, |_, cx| {
|
||||
cx.emit(Event::DiffBaseChanged);
|
||||
cx.emit(BufferEvent::DiffBaseChanged);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
|
@ -1003,7 +1003,7 @@ impl Buffer {
|
|||
this.update(&mut cx, |this, cx| {
|
||||
this.git_diff = buffer_diff;
|
||||
this.non_text_state_update_count += 1;
|
||||
cx.emit(Event::DiffUpdated);
|
||||
cx.emit(BufferEvent::DiffUpdated);
|
||||
})
|
||||
.ok();
|
||||
}))
|
||||
|
@ -1142,7 +1142,7 @@ impl Buffer {
|
|||
self.syntax_map.lock().did_parse(syntax_snapshot);
|
||||
self.request_autoindent(cx);
|
||||
self.parse_status.0.send(ParseStatus::Idle).unwrap();
|
||||
cx.emit(Event::Reparsed);
|
||||
cx.emit(BufferEvent::Reparsed);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1900,9 +1900,9 @@ impl Buffer {
|
|||
|
||||
self.reparse(cx);
|
||||
|
||||
cx.emit(Event::Edited);
|
||||
cx.emit(BufferEvent::Edited);
|
||||
if was_dirty != self.is_dirty() {
|
||||
cx.emit(Event::DirtyChanged);
|
||||
cx.emit(BufferEvent::DirtyChanged);
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -2106,12 +2106,12 @@ impl Buffer {
|
|||
self.non_text_state_update_count += 1;
|
||||
self.text.lamport_clock.observe(lamport_timestamp);
|
||||
cx.notify();
|
||||
cx.emit(Event::DiagnosticsUpdated);
|
||||
cx.emit(BufferEvent::DiagnosticsUpdated);
|
||||
}
|
||||
}
|
||||
|
||||
fn send_operation(&mut self, operation: Operation, cx: &mut ModelContext<Self>) {
|
||||
cx.emit(Event::Operation(operation));
|
||||
cx.emit(BufferEvent::Operation(operation));
|
||||
}
|
||||
|
||||
/// Removes the selections for a given peer.
|
||||
|
@ -2300,7 +2300,7 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for Buffer {}
|
||||
impl EventEmitter<BufferEvent> for Buffer {}
|
||||
|
||||
impl Deref for Buffer {
|
||||
type Target = TextBuffer;
|
||||
|
|
|
@ -275,7 +275,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
|
|||
|buffer, cx| {
|
||||
let buffer_1_events = buffer_1_events.clone();
|
||||
cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
|
||||
Event::Operation(op) => buffer1_ops.lock().push(op),
|
||||
BufferEvent::Operation(op) => buffer1_ops.lock().push(op),
|
||||
event => buffer_1_events.lock().push(event),
|
||||
})
|
||||
.detach();
|
||||
|
@ -313,15 +313,15 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
|
|||
assert_eq!(
|
||||
mem::take(&mut *buffer_1_events.lock()),
|
||||
vec![
|
||||
Event::Edited,
|
||||
Event::DirtyChanged,
|
||||
Event::Edited,
|
||||
Event::Edited,
|
||||
BufferEvent::Edited,
|
||||
BufferEvent::DirtyChanged,
|
||||
BufferEvent::Edited,
|
||||
BufferEvent::Edited,
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
mem::take(&mut *buffer_2_events.lock()),
|
||||
vec![Event::Edited, Event::DirtyChanged]
|
||||
vec![BufferEvent::Edited, BufferEvent::DirtyChanged]
|
||||
);
|
||||
|
||||
buffer1.update(cx, |buffer, cx| {
|
||||
|
@ -336,11 +336,11 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
|
|||
});
|
||||
assert_eq!(
|
||||
mem::take(&mut *buffer_1_events.lock()),
|
||||
vec![Event::Edited, Event::DirtyChanged,]
|
||||
vec![BufferEvent::Edited, BufferEvent::DirtyChanged,]
|
||||
);
|
||||
assert_eq!(
|
||||
mem::take(&mut *buffer_2_events.lock()),
|
||||
vec![Event::Edited, Event::DirtyChanged]
|
||||
vec![BufferEvent::Edited, BufferEvent::DirtyChanged]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2411,7 +2411,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
|||
buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
|
||||
let network = network.clone();
|
||||
cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
|
||||
if let Event::Operation(op) = event {
|
||||
if let BufferEvent::Operation(op) = event {
|
||||
network
|
||||
.lock()
|
||||
.broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
|
||||
|
@ -2539,7 +2539,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
|||
new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
|
||||
let network = network.clone();
|
||||
cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
|
||||
if let Event::Operation(op) = event {
|
||||
if let BufferEvent::Operation(op) = event {
|
||||
network.lock().broadcast(
|
||||
buffer.replica_id(),
|
||||
vec![proto::serialize_operation(op)],
|
||||
|
|
|
@ -270,11 +270,6 @@ impl CachedLspAdapter {
|
|||
.cloned()
|
||||
.unwrap_or_else(|| language_name.lsp_id())
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fn as_fake(&self) -> Option<&FakeLspAdapter> {
|
||||
self.adapter.as_fake()
|
||||
}
|
||||
}
|
||||
|
||||
/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
|
||||
|
@ -508,11 +503,6 @@ pub trait LspAdapter: 'static + Send + Sync {
|
|||
fn language_ids(&self) -> HashMap<String, String> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fn as_fake(&self) -> Option<&FakeLspAdapter> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>(
|
||||
|
@ -751,12 +741,13 @@ where
|
|||
pub struct FakeLspAdapter {
|
||||
pub name: &'static str,
|
||||
pub initialization_options: Option<Value>,
|
||||
pub capabilities: lsp::ServerCapabilities,
|
||||
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
|
||||
pub prettier_plugins: Vec<&'static str>,
|
||||
pub disk_based_diagnostics_progress_token: Option<String>,
|
||||
pub disk_based_diagnostics_sources: Vec<String>,
|
||||
pub prettier_plugins: Vec<&'static str>,
|
||||
pub language_server_binary: LanguageServerBinary,
|
||||
|
||||
pub capabilities: lsp::ServerCapabilities,
|
||||
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
|
||||
}
|
||||
|
||||
/// Configuration of handling bracket pairs for a given language.
|
||||
|
@ -1717,10 +1708,6 @@ impl LspAdapter for FakeLspAdapter {
|
|||
) -> Result<Option<Value>> {
|
||||
Ok(self.initialization_options.clone())
|
||||
}
|
||||
|
||||
fn as_fake(&self) -> Option<&FakeLspAdapter> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option<u32>)]) {
|
||||
|
|
|
@ -99,10 +99,15 @@ struct LanguageRegistryState {
|
|||
reload_count: usize,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fake_server_txs: HashMap<
|
||||
LanguageName,
|
||||
Vec<futures::channel::mpsc::UnboundedSender<lsp::FakeLanguageServer>>,
|
||||
>,
|
||||
fake_server_entries: HashMap<LanguageServerName, FakeLanguageServerEntry>,
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub struct FakeLanguageServerEntry {
|
||||
pub capabilities: lsp::ServerCapabilities,
|
||||
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
|
||||
pub tx: futures::channel::mpsc::UnboundedSender<lsp::FakeLanguageServer>,
|
||||
pub _server: Option<lsp::FakeLanguageServer>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -220,7 +225,7 @@ impl LanguageRegistry {
|
|||
reload_count: 0,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fake_server_txs: Default::default(),
|
||||
fake_server_entries: Default::default(),
|
||||
}),
|
||||
language_server_download_dir: None,
|
||||
lsp_binary_status_tx: Default::default(),
|
||||
|
@ -330,12 +335,35 @@ impl LanguageRegistry {
|
|||
.push(CachedLspAdapter::new(adapter));
|
||||
}
|
||||
|
||||
/// Register a fake language server and adapter
|
||||
/// The returned channel receives a new instance of the language server every time it is started
|
||||
#[cfg(any(feature = "test-support", test))]
|
||||
pub fn register_fake_lsp(
|
||||
&self,
|
||||
language_name: impl Into<LanguageName>,
|
||||
mut adapter: crate::FakeLspAdapter,
|
||||
) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
|
||||
let language_name = language_name.into();
|
||||
let adapter_name = LanguageServerName(adapter.name.into());
|
||||
let capabilities = adapter.capabilities.clone();
|
||||
let initializer = adapter.initializer.take();
|
||||
self.state
|
||||
.write()
|
||||
.lsp_adapters
|
||||
.entry(language_name.clone())
|
||||
.or_default()
|
||||
.push(CachedLspAdapter::new(Arc::new(adapter)));
|
||||
self.register_fake_language_server(adapter_name, capabilities, initializer)
|
||||
}
|
||||
|
||||
/// Register a fake lsp adapter (without the language server)
|
||||
/// The returned channel receives a new instance of the language server every time it is started
|
||||
#[cfg(any(feature = "test-support", test))]
|
||||
pub fn register_fake_lsp_adapter(
|
||||
&self,
|
||||
language_name: impl Into<LanguageName>,
|
||||
adapter: crate::FakeLspAdapter,
|
||||
) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
|
||||
) {
|
||||
let language_name = language_name.into();
|
||||
self.state
|
||||
.write()
|
||||
|
@ -343,21 +371,27 @@ impl LanguageRegistry {
|
|||
.entry(language_name.clone())
|
||||
.or_default()
|
||||
.push(CachedLspAdapter::new(Arc::new(adapter)));
|
||||
self.fake_language_servers(language_name)
|
||||
}
|
||||
|
||||
/// Register a fake language server (without the adapter)
|
||||
/// The returned channel receives a new instance of the language server every time it is started
|
||||
#[cfg(any(feature = "test-support", test))]
|
||||
pub fn fake_language_servers(
|
||||
pub fn register_fake_language_server(
|
||||
&self,
|
||||
language_name: LanguageName,
|
||||
lsp_name: LanguageServerName,
|
||||
capabilities: lsp::ServerCapabilities,
|
||||
initializer: Option<Box<dyn Fn(&mut lsp::FakeLanguageServer) + Send + Sync>>,
|
||||
) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
|
||||
let (servers_tx, servers_rx) = futures::channel::mpsc::unbounded();
|
||||
self.state
|
||||
.write()
|
||||
.fake_server_txs
|
||||
.entry(language_name)
|
||||
.or_default()
|
||||
.push(servers_tx);
|
||||
self.state.write().fake_server_entries.insert(
|
||||
lsp_name,
|
||||
FakeLanguageServerEntry {
|
||||
tx: servers_tx,
|
||||
capabilities,
|
||||
initializer,
|
||||
_server: None,
|
||||
},
|
||||
);
|
||||
servers_rx
|
||||
}
|
||||
|
||||
|
@ -835,8 +869,8 @@ impl LanguageRegistry {
|
|||
adapter.name.0
|
||||
);
|
||||
|
||||
let download_dir = self
|
||||
.language_server_download_dir
|
||||
let download_dir = &self
|
||||
.language_server_download_dir
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("language server download directory has not been assigned before starting server"))
|
||||
.log_err()?;
|
||||
|
@ -877,52 +911,43 @@ impl LanguageRegistry {
|
|||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if true {
|
||||
let capabilities = adapter
|
||||
.as_fake()
|
||||
.map(|fake_adapter| fake_adapter.capabilities.clone())
|
||||
.unwrap_or_else(|| lsp::ServerCapabilities {
|
||||
completion_provider: Some(Default::default()),
|
||||
..Default::default()
|
||||
});
|
||||
if let Some(this) = this.upgrade() {
|
||||
if let Some(fake_entry) = this
|
||||
.state
|
||||
.write()
|
||||
.fake_server_entries
|
||||
.get_mut(&adapter.name)
|
||||
{
|
||||
let (server, mut fake_server) = lsp::FakeLanguageServer::new(
|
||||
server_id,
|
||||
binary,
|
||||
adapter.name.0.to_string(),
|
||||
fake_entry.capabilities.clone(),
|
||||
cx.clone(),
|
||||
);
|
||||
fake_entry._server = Some(fake_server.clone());
|
||||
|
||||
let (server, mut fake_server) = lsp::FakeLanguageServer::new(
|
||||
server_id,
|
||||
binary,
|
||||
adapter.name.0.to_string(),
|
||||
capabilities,
|
||||
cx.clone(),
|
||||
);
|
||||
if let Some(initializer) = &fake_entry.initializer {
|
||||
initializer(&mut fake_server);
|
||||
}
|
||||
|
||||
if let Some(fake_adapter) = adapter.as_fake() {
|
||||
if let Some(initializer) = &fake_adapter.initializer {
|
||||
initializer(&mut fake_server);
|
||||
let tx = fake_entry.tx.clone();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
if fake_server
|
||||
.try_receive_notification::<lsp::notification::Initialized>(
|
||||
)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
tx.unbounded_send(fake_server.clone()).ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
return Ok((server, options));
|
||||
}
|
||||
}
|
||||
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
if fake_server
|
||||
.try_receive_notification::<lsp::notification::Initialized>()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
if let Some(this) = this.upgrade() {
|
||||
if let Some(txs) = this
|
||||
.state
|
||||
.write()
|
||||
.fake_server_txs
|
||||
.get_mut(&_language_name_for_tests)
|
||||
{
|
||||
for tx in txs {
|
||||
tx.unbounded_send(fake_server.clone()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
return Ok((server, options));
|
||||
}
|
||||
|
||||
drop(this);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue