diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 7d6199b1095b1ecf78568f981b1e9adb9926c4d0..bdf43124d408e92dbb0609d125517af194e1dce8 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3222,14 +3222,18 @@ async fn test_canceling_buffer_opening( .unwrap(); // Open a buffer as client B but cancel after a random amount of time. - let buffer_b = project_b.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx)); + let buffer_b = project_b.update(cx_b, |p, cx| { + p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx) + }); deterministic.simulate_random_delay().await; drop(buffer_b); // Try opening the same buffer again as client B, and ensure we can // still do it despite the cancellation above. let buffer_b = project_b - .update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx)) + .update(cx_b, |p, cx| { + p.open_buffer_by_id(buffer_a.read_with(cx_a, |a, _| a.remote_id()), cx) + }) .await .unwrap(); buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc")); diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index ea25355065979593b9c61557a24ce1693a9f5142..e51cad8e509b4ba183efda6e638c1906fb067a5e 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -126,7 +126,7 @@ impl CopilotServer { struct RunningCopilotServer { lsp: Arc, sign_in_status: SignInStatus, - registered_buffers: HashMap, + registered_buffers: HashMap, } #[derive(Clone, Debug)] @@ -162,7 +162,7 @@ impl Status { } struct RegisteredBuffer { - id: usize, + id: u64, uri: lsp::Url, language_id: String, snapshot: BufferSnapshot, @@ -267,7 +267,7 @@ pub struct Copilot { http: Arc, node_runtime: Arc, server: CopilotServer, - buffers: HashMap>, + buffers: HashMap>, } impl Entity for Copilot { @@ -582,7 +582,7 @@ impl Copilot { } pub fn register_buffer(&mut self, buffer: &ModelHandle, cx: &mut ModelContext) { - let buffer_id = buffer.id(); + let buffer_id = buffer.read(cx).remote_id(); self.buffers.insert(buffer_id, buffer.downgrade()); if let CopilotServer::Running(RunningCopilotServer { @@ -596,7 +596,8 @@ impl Copilot { return; } - registered_buffers.entry(buffer.id()).or_insert_with(|| { + let buffer_id = buffer.read(cx).remote_id(); + registered_buffers.entry(buffer_id).or_insert_with(|| { let uri: lsp::Url = uri_for_buffer(buffer, cx); let language_id = id_for_language(buffer.read(cx).language()); let snapshot = buffer.read(cx).snapshot(); @@ -641,7 +642,8 @@ impl Copilot { cx: &mut ModelContext, ) -> Result<()> { if let Ok(server) = self.server.as_running() { - if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.id()) { + let buffer_id = buffer.read(cx).remote_id(); + if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer_id) { match event { language::Event::Edited => { let _ = registered_buffer.report_changes(&buffer, cx); @@ -695,7 +697,7 @@ impl Copilot { Ok(()) } - fn unregister_buffer(&mut self, buffer_id: usize) { + fn unregister_buffer(&mut self, buffer_id: u64) { if let Ok(server) = self.server.as_running() { if let Some(buffer) = server.registered_buffers.remove(&buffer_id) { server @@ -800,7 +802,8 @@ impl Copilot { Err(error) => return Task::ready(Err(error)), }; let lsp = server.lsp.clone(); - let registered_buffer = server.registered_buffers.get_mut(&buffer.id()).unwrap(); + let buffer_id = buffer.read(cx).remote_id(); + let registered_buffer = server.registered_buffers.get_mut(&buffer_id).unwrap(); let snapshot = registered_buffer.report_changes(buffer, cx); let buffer = buffer.read(cx); let uri = registered_buffer.uri.clone(); @@ -919,7 +922,9 @@ fn uri_for_buffer(buffer: &ModelHandle, cx: &AppContext) -> lsp::Url { if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) { lsp::Url::from_file_path(file.abs_path(cx)).unwrap() } else { - format!("buffer://{}", buffer.id()).parse().unwrap() + format!("buffer://{}", buffer.read(cx).remote_id()) + .parse() + .unwrap() } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8771387cb73cb456c1416b1e5d0575b02648d1c4..5568ed79b5c74ebb21926b315cfb6796b5271cfa 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2594,7 +2594,7 @@ impl Editor { let old_text = buffer.text_for_range(old_range.clone()).collect::(); let newest_selection = self.selections.newest_anchor(); - if newest_selection.start.buffer_id != Some(buffer_handle.id()) { + if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) { return None; } @@ -2802,7 +2802,7 @@ impl Editor { ), ); } - multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t))); + multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx); multibuffer }); @@ -5764,7 +5764,7 @@ impl Editor { cx: &mut ViewContext, ) { // If there are multiple definitions, open them in a multibuffer - locations.sort_by_key(|location| location.buffer.id()); + locations.sort_by_key(|location| location.buffer.read(cx).remote_id()); let mut locations = locations.into_iter().peekable(); let mut ranges_to_highlight = Vec::new(); @@ -6059,7 +6059,7 @@ impl Editor { buffer.update(&mut cx, |buffer, cx| { if let Some(transaction) = transaction { if !buffer.is_singleton() { - buffer.push_transaction(&transaction.0); + buffer.push_transaction(&transaction.0, cx); } } diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index dcd49607fb95186caf302e8a29b7fb720b202451..df93326e9af443d96553200b5ec22aeb82618456 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -704,10 +704,10 @@ impl Item for Editor { this.update(&mut cx, |editor, cx| { editor.request_autoscroll(Autoscroll::fit(), cx) })?; - buffer.update(&mut cx, |buffer, _| { + buffer.update(&mut cx, |buffer, cx| { if let Some(transaction) = transaction { if !buffer.is_singleton() { - buffer.push_transaction(&transaction.0); + buffer.push_transaction(&transaction.0, cx); } } }); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 39e66d0f93c15f23da9ff93c9f7e5be5a460c2b4..a2160b47e5d342122b57f964f2070cdc8010684a 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -43,7 +43,7 @@ pub struct ExcerptId(usize); pub struct MultiBuffer { snapshot: RefCell, - buffers: RefCell>, + buffers: RefCell>, next_excerpt_id: usize, subscriptions: Topic, singleton: bool, @@ -85,7 +85,7 @@ struct History { #[derive(Clone)] struct Transaction { id: TransactionId, - buffer_transactions: HashMap, + buffer_transactions: HashMap, first_edit_at: Instant, last_edit_at: Instant, suppress_grouping: bool, @@ -145,7 +145,7 @@ pub struct ExcerptBoundary { struct Excerpt { id: ExcerptId, locator: Locator, - buffer_id: usize, + buffer_id: u64, buffer: BufferSnapshot, range: ExcerptRange, max_buffer_row: u32, @@ -337,7 +337,7 @@ impl MultiBuffer { offset: T, theme: Option<&SyntaxTheme>, cx: &AppContext, - ) -> Option<(usize, Vec>)> { + ) -> Option<(u64, Vec>)> { self.read(cx).symbols_containing(offset, theme) } @@ -394,7 +394,7 @@ impl MultiBuffer { is_insertion: bool, original_indent_column: u32, } - let mut buffer_edits: HashMap> = Default::default(); + let mut buffer_edits: HashMap> = Default::default(); let mut cursor = snapshot.excerpts.cursor::(); for (ix, (range, new_text)) in edits.enumerate() { let new_text: Arc = new_text.into(); @@ -593,7 +593,7 @@ impl MultiBuffer { if let Some(transaction_id) = buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) { - buffer_transactions.insert(buffer.id(), transaction_id); + buffer_transactions.insert(buffer.read(cx).remote_id(), transaction_id); } } @@ -614,12 +614,12 @@ impl MultiBuffer { } } - pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T) + pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &mut ModelContext) where T: IntoIterator, &'a language::Transaction)>, { self.history - .push_transaction(buffer_transactions, Instant::now()); + .push_transaction(buffer_transactions, Instant::now(), cx); self.history.finalize_last_transaction(); } @@ -644,7 +644,7 @@ impl MultiBuffer { cursor_shape: CursorShape, cx: &mut ModelContext, ) { - let mut selections_by_buffer: HashMap>> = + let mut selections_by_buffer: HashMap>> = Default::default(); let snapshot = self.read(cx); let mut cursor = snapshot.excerpts.cursor::>(); @@ -785,8 +785,8 @@ impl MultiBuffer { let (mut tx, rx) = mpsc::channel(256); let task = cx.spawn(|this, mut cx| async move { for (buffer, ranges) in excerpts { - let buffer_id = buffer.id(); - let buffer_snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot()); + let (buffer_id, buffer_snapshot) = + buffer.read_with(&cx, |buffer, _| (buffer.remote_id(), buffer.snapshot())); let mut excerpt_ranges = Vec::new(); let mut range_counts = Vec::new(); @@ -855,7 +855,7 @@ impl MultiBuffer { where O: text::ToPoint + text::ToOffset, { - let buffer_id = buffer.id(); + let buffer_id = buffer.read(cx).remote_id(); let buffer_snapshot = buffer.read(cx).snapshot(); let (excerpt_ranges, range_counts) = build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count); @@ -924,7 +924,7 @@ impl MultiBuffer { self.sync(cx); - let buffer_id = buffer.id(); + let buffer_id = buffer.read(cx).remote_id(); let buffer_snapshot = buffer.read(cx).snapshot(); let mut buffers = self.buffers.borrow_mut(); @@ -1051,7 +1051,7 @@ impl MultiBuffer { let buffers = self.buffers.borrow(); let mut cursor = snapshot.excerpts.cursor::>(); for locator in buffers - .get(&buffer.id()) + .get(&buffer.read(cx).remote_id()) .map(|state| &state.excerpts) .into_iter() .flatten() @@ -1321,7 +1321,7 @@ impl MultiBuffer { .collect() } - pub fn buffer(&self, buffer_id: usize) -> Option> { + pub fn buffer(&self, buffer_id: u64) -> Option> { self.buffers .borrow() .get(&buffer_id) @@ -1478,8 +1478,8 @@ impl MultiBuffer { for (locator, buffer, buffer_edited) in excerpts_to_edit { new_excerpts.push_tree(cursor.slice(&Some(locator), Bias::Left, &()), &()); let old_excerpt = cursor.item().unwrap(); - let buffer_id = buffer.id(); let buffer = buffer.read(cx); + let buffer_id = buffer.remote_id(); let mut new_excerpt; if buffer_edited { @@ -1605,11 +1605,11 @@ impl MultiBuffer { let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() { let text = RandomCharIter::new(&mut *rng).take(10).collect::(); buffers.push(cx.add_model(|cx| Buffer::new(0, text, cx))); - let buffer = buffers.last().unwrap(); + let buffer = buffers.last().unwrap().read(cx); log::info!( "Creating new buffer {} with text: {:?}", - buffer.id(), - buffer.read(cx).text() + buffer.remote_id(), + buffer.text() ); buffers.last().unwrap().clone() } else { @@ -1637,7 +1637,7 @@ impl MultiBuffer { .collect::>(); log::info!( "Inserting excerpts from buffer {} and ranges {:?}: {:?}", - buffer_handle.id(), + buffer_handle.read(cx).remote_id(), ranges.iter().map(|r| &r.context).collect::>(), ranges .iter() @@ -1830,7 +1830,7 @@ impl MultiBufferSnapshot { (start..end, word_kind) } - pub fn as_singleton(&self) -> Option<(&ExcerptId, usize, &BufferSnapshot)> { + pub fn as_singleton(&self) -> Option<(&ExcerptId, u64, &BufferSnapshot)> { if self.singleton { self.excerpts .iter() @@ -2938,7 +2938,7 @@ impl MultiBufferSnapshot { &self, offset: T, theme: Option<&SyntaxTheme>, - ) -> Option<(usize, Vec>)> { + ) -> Option<(u64, Vec>)> { let anchor = self.anchor_before(offset); let excerpt_id = anchor.excerpt_id(); let excerpt = self.excerpt(excerpt_id)?; @@ -2978,7 +2978,7 @@ impl MultiBufferSnapshot { } } - pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option { + pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option { Some(self.excerpt(excerpt_id)?.buffer_id) } @@ -3116,7 +3116,7 @@ impl History { fn end_transaction( &mut self, now: Instant, - buffer_transactions: HashMap, + buffer_transactions: HashMap, ) -> bool { assert_ne!(self.transaction_depth, 0); self.transaction_depth -= 1; @@ -3141,8 +3141,12 @@ impl History { } } - fn push_transaction<'a, T>(&mut self, buffer_transactions: T, now: Instant) - where + fn push_transaction<'a, T>( + &mut self, + buffer_transactions: T, + now: Instant, + cx: &mut ModelContext, + ) where T: IntoIterator, &'a language::Transaction)>, { assert_eq!(self.transaction_depth, 0); @@ -3150,7 +3154,7 @@ impl History { id: self.next_transaction_id.tick(), buffer_transactions: buffer_transactions .into_iter() - .map(|(buffer, transaction)| (buffer.id(), transaction.id)) + .map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id)) .collect(), first_edit_at: now, last_edit_at: now, @@ -3247,7 +3251,7 @@ impl Excerpt { fn new( id: ExcerptId, locator: Locator, - buffer_id: usize, + buffer_id: u64, buffer: BufferSnapshot, range: ExcerptRange, has_trailing_newline: bool, @@ -4715,7 +4719,7 @@ mod tests { "Inserting excerpt at {} of {} for buffer {}: {:?}[{:?}] = {:?}", excerpt_ix, expected_excerpts.len(), - buffer_handle.id(), + buffer_handle.read(cx).remote_id(), buffer.text(), start_ix..end_ix, &buffer.text()[start_ix..end_ix] @@ -4801,8 +4805,8 @@ mod tests { let mut excerpt_starts = excerpt_starts.into_iter(); for (buffer, range) in &expected_excerpts { - let buffer_id = buffer.id(); let buffer = buffer.read(cx); + let buffer_id = buffer.remote_id(); let buffer_range = range.to_offset(buffer); let buffer_start_point = buffer.offset_to_point(buffer_range.start); let buffer_start_point_utf16 = diff --git a/crates/editor/src/multi_buffer/anchor.rs b/crates/editor/src/multi_buffer/anchor.rs index 4ecab76c48d7f469b4b844b821dd93c97a44dcb9..9a5145c244880e37cd27992886d7a4740f5b902b 100644 --- a/crates/editor/src/multi_buffer/anchor.rs +++ b/crates/editor/src/multi_buffer/anchor.rs @@ -8,7 +8,7 @@ use sum_tree::Bias; #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] pub struct Anchor { - pub(crate) buffer_id: Option, + pub(crate) buffer_id: Option, pub(crate) excerpt_id: ExcerptId, pub(crate) text_anchor: text::Anchor, } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 32cbc260ec126d29e878d526d0db81aeec9b2f98..f56f6035f629b0f3a9ac9fd5d942e5ff46d8060b 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -357,20 +357,6 @@ impl Buffer { ) } - pub fn from_file>( - replica_id: ReplicaId, - base_text: T, - diff_base: Option, - file: Arc, - cx: &mut ModelContext, - ) -> Self { - Self::build( - TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()), - diff_base.map(|h| h.into().into_boxed_str().into()), - Some(file), - ) - } - pub fn from_proto( replica_id: ReplicaId, message: proto::BufferState, @@ -460,7 +446,11 @@ impl Buffer { self } - fn build(buffer: TextBuffer, diff_base: Option, file: Option>) -> Self { + pub fn build( + buffer: TextBuffer, + diff_base: Option, + file: Option>, + ) -> Self { let saved_mtime = if let Some(file) = file.as_ref() { file.mtime() } else { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 22661ac92569b0a7349166368457173d1ec5fff3..40687ce0a7fa518f61896ac5b42866086edb78a4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -109,6 +109,7 @@ pub struct Project { collaborators: HashMap, client_subscriptions: Vec, _subscriptions: Vec, + next_buffer_id: u64, opened_buffer: (watch::Sender<()>, watch::Receiver<()>), shared_buffers: HashMap>, #[allow(clippy::type_complexity)] @@ -124,7 +125,7 @@ pub struct Project { /// Used for re-issuing buffer requests when peers temporarily disconnect incomplete_remote_buffers: HashMap>>, buffer_snapshots: HashMap>>, // buffer_id -> server_id -> vec of snapshots - buffers_being_formatted: HashSet, + buffers_being_formatted: HashSet, nonce: u128, _maintain_buffer_languages: Task<()>, _maintain_workspace_config: Task<()>, @@ -441,6 +442,7 @@ impl Project { worktrees: Default::default(), buffer_ordered_messages_tx: tx, collaborators: Default::default(), + next_buffer_id: 0, opened_buffers: Default::default(), shared_buffers: Default::default(), incomplete_remote_buffers: Default::default(), @@ -509,6 +511,7 @@ impl Project { worktrees: Vec::new(), buffer_ordered_messages_tx: tx, loading_buffers_by_path: Default::default(), + next_buffer_id: 0, opened_buffer: watch::channel(), shared_buffers: Default::default(), incomplete_remote_buffers: Default::default(), @@ -1401,9 +1404,10 @@ impl Project { worktree: &ModelHandle, cx: &mut ModelContext, ) -> Task>> { + let buffer_id = post_inc(&mut self.next_buffer_id); let load_buffer = worktree.update(cx, |worktree, cx| { let worktree = worktree.as_local_mut().unwrap(); - worktree.load_buffer(path, cx) + worktree.load_buffer(buffer_id, path, cx) }); cx.spawn(|this, mut cx| async move { let buffer = load_buffer.await?; @@ -3200,9 +3204,11 @@ impl Project { cx.spawn(|this, mut cx| async move { // Do not allow multiple concurrent formatting requests for the // same buffer. - this.update(&mut cx, |this, _| { - buffers_with_paths_and_servers - .retain(|(buffer, _, _)| this.buffers_being_formatted.insert(buffer.id())); + this.update(&mut cx, |this, cx| { + buffers_with_paths_and_servers.retain(|(buffer, _, _)| { + this.buffers_being_formatted + .insert(buffer.read(cx).remote_id()) + }); }); let _cleanup = defer({ @@ -3210,9 +3216,10 @@ impl Project { let mut cx = cx.clone(); let buffers = &buffers_with_paths_and_servers; move || { - this.update(&mut cx, |this, _| { + this.update(&mut cx, |this, cx| { for (buffer, _, _) in buffers { - this.buffers_being_formatted.remove(&buffer.id()); + this.buffers_being_formatted + .remove(&buffer.read(cx).remote_id()); } }); } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 14fb4f26287e65863945008a64ded26c5ed4d2b6..1281ddeff3f86607cbe5a25990a3171aaab0913e 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -506,6 +506,7 @@ impl LocalWorktree { pub(crate) fn load_buffer( &mut self, + id: u64, path: &Path, cx: &mut ModelContext, ) -> Task>> { @@ -514,8 +515,12 @@ impl LocalWorktree { let (file, contents, diff_base) = this .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; + let text_buffer = cx + .background() + .spawn(async move { text::Buffer::new(0, id, contents) }) + .await; Ok(cx.add_model(|cx| { - let mut buffer = Buffer::from_file(0, contents, diff_base, Arc::new(file), cx); + let mut buffer = Buffer::build(text_buffer, diff_base, Some(Arc::new(file))); buffer.git_diff_recalc(cx); buffer })) diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 3df83fa305fa92e989ea124db433706df7c9c959..86bb7f4a26768260c1be6c74d7392d8aa6fe0236 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -90,8 +90,7 @@ impl HistoryEntry { } struct History { - // TODO: Turn this into a String or Rope, maybe. - base_text: Arc, + base_text: Rope, operations: TreeMap, insertion_slices: HashMap>, undo_stack: Vec, @@ -107,7 +106,7 @@ struct InsertionSlice { } impl History { - pub fn new(base_text: Arc) -> Self { + pub fn new(base_text: Rope) -> Self { Self { base_text, operations: Default::default(), @@ -470,7 +469,7 @@ impl Buffer { let line_ending = LineEnding::detect(&base_text); LineEnding::normalize(&mut base_text); - let history = History::new(base_text.into()); + let history = History::new(Rope::from(base_text.as_ref())); let mut fragments = SumTree::new(); let mut insertions = SumTree::new(); @@ -478,7 +477,7 @@ impl Buffer { let mut lamport_clock = clock::Lamport::new(replica_id); let mut version = clock::Global::new(); - let visible_text = Rope::from(history.base_text.as_ref()); + let visible_text = history.base_text.clone(); if !visible_text.is_empty() { let insertion_timestamp = InsertionTimestamp { replica_id: 0, @@ -1165,7 +1164,7 @@ impl Buffer { self.history.group_until(transaction_id); } - pub fn base_text(&self) -> &Arc { + pub fn base_text(&self) -> &Rope { &self.history.base_text } diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 38e6b92b344abacec22f641ee325d44cd213d354..43c4544611e9f174db5d8d2350501020eaa6d84e 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -60,7 +60,7 @@ pub trait Item: View { style: &theme::Tab, cx: &AppContext, ) -> AnyElement; - fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project::Item)) {} + fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project::Item)) {} // (model id, Item) fn is_singleton(&self, _cx: &AppContext) -> bool { false }