diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 907b594ec506cf..98c4cc5e649a97 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -49,7 +49,7 @@ use std::{ num::NonZeroU32, ops::{Deref, DerefMut, Range}, path::{Path, PathBuf}, - str, + rc, str, sync::{Arc, LazyLock}, time::{Duration, Instant}, vec, @@ -125,6 +125,7 @@ pub struct Buffer { /// Memoize calls to has_changes_since(saved_version). /// The contents of a cell are (self.version, has_changes) at the time of a last call. has_unsaved_edits: Cell<(clock::Global, bool)>, + change_bits: Vec>>, _subscriptions: Vec, } @@ -978,6 +979,7 @@ impl Buffer { completion_triggers_timestamp: Default::default(), deferred_ops: OperationQueue::new(), has_conflict: false, + change_bits: Default::default(), _subscriptions: Vec::new(), } } @@ -1252,6 +1254,7 @@ impl Buffer { self.non_text_state_update_count += 1; self.syntax_map.lock().clear(&self.text); self.language = language; + self.was_changed(); self.reparse(cx); cx.emit(BufferEvent::LanguageChanged); } @@ -1286,6 +1289,7 @@ impl Buffer { .set((self.saved_version().clone(), false)); self.has_conflict = false; self.saved_mtime = mtime; + self.was_changed(); cx.emit(BufferEvent::Saved); cx.notify(); } @@ -1381,6 +1385,7 @@ impl Buffer { self.file = Some(new_file); if file_changed { + self.was_changed(); self.non_text_state_update_count += 1; if was_dirty != self.is_dirty() { cx.emit(BufferEvent::DirtyChanged); @@ -1958,6 +1963,23 @@ impl Buffer { self.text.subscribe() } + /// Adds a bit to the list of bits that are set when the buffer's text changes. + /// + /// This allows downstream code to check if the buffer's text has changed without + /// waiting for an effect cycle, which would be required if using eents. + pub fn record_changes(&mut self, bit: rc::Weak>) { + self.change_bits.push(bit); + } + + fn was_changed(&mut self) { + self.change_bits.retain(|change_bit| { + change_bit.upgrade().map_or(false, |bit| { + bit.replace(true); + true + }) + }); + } + /// Starts a transaction, if one is not already in-progress. When undoing or /// redoing edits, all of the edits performed within a transaction are undone /// or redone together. @@ -2368,6 +2390,7 @@ impl Buffer { } self.text.apply_ops(buffer_ops); self.deferred_ops.insert(deferred_ops); + self.was_changed(); self.flush_deferred_ops(cx); self.did_edit(&old_version, was_dirty, cx); // Notify independently of whether the buffer was edited as the operations could include a @@ -2502,7 +2525,8 @@ impl Buffer { } } - fn send_operation(&self, operation: Operation, is_local: bool, cx: &mut Context) { + fn send_operation(&mut self, operation: Operation, is_local: bool, cx: &mut Context) { + self.was_changed(); cx.emit(BufferEvent::Operation { operation, is_local, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index e5f5a23b0cccf2..8f7a30e3a1b7c5 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -31,7 +31,7 @@ use smol::future::yield_now; use std::{ any::type_name, borrow::Cow, - cell::{Ref, RefCell}, + cell::{Cell, Ref, RefCell}, cmp, fmt, future::Future, io, @@ -39,6 +39,7 @@ use std::{ mem, ops::{Range, RangeBounds, Sub}, path::Path, + rc::Rc, str, sync::Arc, time::{Duration, Instant}, @@ -76,6 +77,7 @@ pub struct MultiBuffer { history: History, title: Option, capability: Capability, + buffer_changed_since_sync: Rc>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -568,6 +570,7 @@ impl MultiBuffer { capability, title: None, buffers_by_path: Default::default(), + buffer_changed_since_sync: Default::default(), history: History { next_transaction_id: clock::Lamport::default(), undo_stack: Vec::new(), @@ -587,6 +590,7 @@ impl MultiBuffer { subscriptions: Default::default(), singleton: false, capability, + buffer_changed_since_sync: Default::default(), history: History { next_transaction_id: Default::default(), undo_stack: Default::default(), @@ -600,7 +604,11 @@ impl MultiBuffer { pub fn clone(&self, new_cx: &mut Context) -> Self { let mut buffers = HashMap::default(); + let buffer_changed_since_sync = Rc::new(Cell::new(false)); for (buffer_id, buffer_state) in self.buffers.borrow().iter() { + buffer_state.buffer.update(new_cx, |buffer, _| { + buffer.record_changes(Rc::downgrade(&buffer_changed_since_sync)); + }); buffers.insert( *buffer_id, BufferState { @@ -629,6 +637,7 @@ impl MultiBuffer { capability: self.capability, history: self.history.clone(), title: self.title.clone(), + buffer_changed_since_sync, } } @@ -1728,19 +1737,25 @@ impl MultiBuffer { self.sync(cx); - let buffer_id = buffer.read(cx).remote_id(); let buffer_snapshot = buffer.read(cx).snapshot(); + let buffer_id = buffer_snapshot.remote_id(); let mut buffers = self.buffers.borrow_mut(); - let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState { - last_version: buffer_snapshot.version().clone(), - last_non_text_state_update_count: buffer_snapshot.non_text_state_update_count(), - excerpts: Default::default(), - _subscriptions: [ - cx.observe(&buffer, |_, _, cx| cx.notify()), - cx.subscribe(&buffer, Self::on_buffer_event), - ], - buffer: buffer.clone(), + let buffer_state = buffers.entry(buffer_id).or_insert_with(|| { + self.buffer_changed_since_sync.replace(true); + buffer.update(cx, |buffer, _| { + buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync)); + }); + BufferState { + last_version: buffer_snapshot.version().clone(), + last_non_text_state_update_count: buffer_snapshot.non_text_state_update_count(), + excerpts: Default::default(), + _subscriptions: [ + cx.observe(&buffer, |_, _, cx| cx.notify()), + cx.subscribe(&buffer, Self::on_buffer_event), + ], + buffer: buffer.clone(), + } }); let mut snapshot = self.snapshot.borrow_mut(); @@ -2236,6 +2251,7 @@ impl MultiBuffer { cx: &mut Context, ) { self.sync(cx); + self.buffer_changed_since_sync.replace(true); let diff = diff.read(cx); let buffer_id = diff.buffer_id; @@ -2714,6 +2730,11 @@ impl MultiBuffer { } fn sync(&self, cx: &App) { + let changed = self.buffer_changed_since_sync.replace(false); + if !changed { + return; + } + let mut snapshot = self.snapshot.borrow_mut(); let mut excerpts_to_edit = Vec::new(); let mut non_text_state_updated = false; diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index c8658406348823..476ff01e8d0b31 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2424,14 +2424,10 @@ impl Pane { .child(label), ); - let single_entry_to_resolve = { - let item_entries = self.items[ix].project_entry_ids(cx); - if item_entries.len() == 1 { - Some(item_entries[0]) - } else { - None - } - }; + let single_entry_to_resolve = self.items[ix] + .is_singleton(cx) + .then(|| self.items[ix].project_entry_ids(cx).get(0).copied()) + .flatten(); let total_items = self.items.len(); let has_items_to_left = ix > 0;