From 7abb63cfda824cb79c721da993db559dcd54c00b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 10 Apr 2024 07:32:51 -0700 Subject: [PATCH] Improve the ergonomics of creating local buffers (#10347) This PR renames `language::Buffer::new` to `language::Buffer::local` and simplifies its interface. Instead of taking a replica id (which should always be 0 for the local case) and a `BufferId`, which was awkward and verbose to construct, it simply takes text and a `cx`. It uses the `cx` to derive a `BufferId` from the `EntityId` associated with the `cx`, which should always be positive based on the following analysis... We convert the entity id to a u64 using this method on `EntityId`, which is defined by macros in the `slotmap` crate: ```rust pub fn as_ffi(self) -> u64 { (u64::from(self.version.get()) << 32) | u64::from(self.idx) } ``` If you look at the type of `version` in `KeyData`, it is non-zero: ```rust #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct KeyData { idx: u32, version: NonZeroU32, } ``` This commit also adds `Context::reserve_model` and `Context::insert_model` to determine a model's entity ID before it is created, which we need in order to assign a `BufferId` in the background when loading a buffer asynchronously. Release Notes: - N/A --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> --- crates/assistant/src/assistant_panel.rs | 10 +- crates/assistant/src/codegen.rs | 19 +-- crates/copilot/src/copilot.rs | 12 +- .../src/copilot_completion_provider.rs | 18 +- crates/editor/src/display_map.rs | 16 +- crates/editor/src/editor.rs | 24 +-- crates/editor/src/editor_tests.rs | 139 +++------------- crates/editor/src/movement.rs | 9 +- crates/editor/src/test/editor_test_context.rs | 6 +- crates/gpui/src/app.rs | 46 +++-- crates/gpui/src/app/async_context.rs | 53 +++++- crates/gpui/src/app/entity_map.rs | 12 +- crates/gpui/src/app/model_context.rs | 29 +++- crates/gpui/src/app/test_context.rs | 26 +++ crates/gpui/src/gpui.rs | 24 +++ crates/gpui/src/window.rs | 62 ++++--- crates/gpui/src/window/element_cx.rs | 12 ++ crates/language/src/buffer.rs | 8 +- crates/language/src/buffer_tests.rs | 157 +++++++----------- crates/languages/src/c.rs | 4 +- crates/languages/src/css.rs | 7 +- crates/languages/src/python.rs | 4 +- crates/languages/src/rust.rs | 4 +- crates/languages/src/typescript.rs | 7 +- crates/multi_buffer/src/multi_buffer.rs | 110 +++--------- crates/project/src/project.rs | 11 +- crates/search/src/buffer_search.rs | 23 +-- crates/text/src/text.rs | 7 + crates/vim/src/editor_events.rs | 6 +- crates/worktree/src/worktree.rs | 13 +- crates/worktree/src/worktree_tests.rs | 17 +- 31 files changed, 376 insertions(+), 519 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 435388cf81..041bd2f62d 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -31,7 +31,7 @@ use gpui::{ StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext, }; -use language::{language_settings::SoftWrap, Buffer, BufferId, LanguageRegistry, ToOffset as _}; +use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _}; use parking_lot::Mutex; use project::Project; use search::{buffer_search::DivRegistrar, BufferSearchBar}; @@ -1310,7 +1310,7 @@ impl Conversation { ) -> Self { let markdown = language_registry.language_for_name("Markdown"); let buffer = cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), ""); + let mut buffer = Buffer::local("", cx); buffer.set_language_registry(language_registry); cx.spawn(|buffer, mut cx| async move { let markdown = markdown.await?; @@ -1398,11 +1398,7 @@ impl Conversation { let mut message_anchors = Vec::new(); let mut next_message_id = MessageId(0); let buffer = cx.new_model(|cx| { - let mut buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - saved_conversation.text, - ); + let mut buffer = Buffer::local(saved_conversation.text, cx); for message in saved_conversation.messages { message_anchors.push(MessageAnchor { id: message.id, diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index 18672d6455..5a6427467c 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -361,8 +361,8 @@ mod tests { use gpui::{Context, TestAppContext}; use indoc::indoc; use language::{ - language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, - LanguageMatcher, Point, + language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher, + Point, }; use rand::prelude::*; use serde::Serialize; @@ -388,9 +388,8 @@ mod tests { } } "}; - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let range = buffer.read_with(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); @@ -447,9 +446,8 @@ mod tests { le } "}; - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let position = buffer.read_with(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); @@ -506,9 +504,8 @@ mod tests { " \n", "}\n" // ); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let position = buffer.read_with(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 7049af0e43..d82eb8cc78 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -1042,9 +1042,7 @@ mod tests { async fn test_buffer_management(cx: &mut TestAppContext) { let (copilot, mut lsp) = Copilot::fake(cx); - let buffer_1 = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "Hello") - }); + let buffer_1 = cx.new_model(|cx| Buffer::local("Hello", cx)); let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64()) .parse() .unwrap(); @@ -1062,13 +1060,7 @@ mod tests { } ); - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "Goodbye", - ) - }); + let buffer_2 = cx.new_model(|cx| Buffer::local("Goodbye", cx)); let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64()) .parse() .unwrap(); diff --git a/crates/copilot_ui/src/copilot_completion_provider.rs b/crates/copilot_ui/src/copilot_completion_provider.rs index ac9f2511fe..c6226c7bb1 100644 --- a/crates/copilot_ui/src/copilot_completion_provider.rs +++ b/crates/copilot_ui/src/copilot_completion_provider.rs @@ -272,7 +272,7 @@ mod tests { use indoc::indoc; use language::{ language_settings::{AllLanguageSettings, AllLanguageSettingsContent}, - BufferId, Point, + Point, }; use project::Project; use serde_json::json; @@ -729,20 +729,8 @@ mod tests { let (copilot, copilot_lsp) = Copilot::fake(cx); - let buffer_1 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "a = 1\nb = 2\n", - ) - }); - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "c = 3\nd = 4\n", - ) - }); + let buffer_1 = cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local("c = 3\nd = 4\n", cx)); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, language::Capability::ReadWrite); multibuffer.push_excerpts( diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 014c570238..efae6a030d 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -984,7 +984,6 @@ pub mod tests { use settings::SettingsStore; use smol::stream::StreamExt; use std::{env, sync::Arc}; - use text::BufferId; use theme::{LoadThemes, SyntaxTheme}; use util::test::{marked_text_ranges, sample_text}; use Bias::*; @@ -1445,10 +1444,7 @@ pub mod tests { cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap()))); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); @@ -1533,10 +1529,7 @@ pub mod tests { cx.update(|cx| init_test(cx, |_| {})); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); @@ -1602,10 +1595,7 @@ pub mod tests { let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d603bfd155..710e040e96 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1328,37 +1328,19 @@ impl InlayHintRefreshReason { impl Editor { pub fn single_line(cx: &mut ViewContext) -> Self { - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - String::new(), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local("", cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); Self::new(EditorMode::SingleLine, buffer, None, cx) } pub fn multi_line(cx: &mut ViewContext) -> Self { - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - String::new(), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local("", cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); Self::new(EditorMode::Full, buffer, None, cx) } pub fn auto_height(max_lines: usize, cx: &mut ViewContext) -> Self { - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - String::new(), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local("", cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx) } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 25e144af8c..8e9e9d7299 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -38,8 +38,7 @@ fn test_edit_events(cx: &mut TestAppContext) { init_test(cx, |_| {}); let buffer = cx.new_model(|cx| { - let mut buffer = - language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456"); + let mut buffer = language::Buffer::local("123456", cx); buffer.set_group_interval(Duration::from_secs(1)); buffer }); @@ -154,9 +153,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { init_test(cx, |_| {}); let mut now = Instant::now(); - let buffer = cx.new_model(|cx| { - language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456") - }); + let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx)); let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval()); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx)); @@ -227,8 +224,7 @@ fn test_ime_composition(cx: &mut TestAppContext) { init_test(cx, |_| {}); let buffer = cx.new_model(|cx| { - let mut buffer = - language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcde"); + let mut buffer = language::Buffer::local("abcde", cx); // Ensure automatic grouping doesn't occur. buffer.set_group_interval(Duration::ZERO); buffer @@ -2344,21 +2340,10 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) { None, )); - let toml_buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "a = 1\nb = 2\n", - ) - .with_language(toml_language, cx) - }); + let toml_buffer = + cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx)); let rust_buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "const c: usize = 3;\n", - ) - .with_language(rust_language, cx) + Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx) }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); @@ -4304,10 +4289,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); @@ -4471,10 +4453,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { let text = "fn a() {}"; - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); editor @@ -5129,10 +5108,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); view.condition::(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) @@ -5280,10 +5256,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); editor @@ -5470,10 +5443,7 @@ async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) { Some(tree_sitter_rust::language()), )); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); editor @@ -6939,13 +6909,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(3, 4, 'a'), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx)); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); multibuffer.push_excerpts( @@ -7029,13 +6993,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { primary: None, } }); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - initial_text, - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx)); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); multibuffer.push_excerpts(buffer, excerpt_ranges, cx); @@ -7093,13 +7051,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { fn test_refresh_selections(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(3, 4, 'a'), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx)); let mut excerpt1_id = None; let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); @@ -7184,13 +7136,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) { fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(3, 4, 'a'), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx)); let mut excerpt1_id = None; let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); @@ -7285,10 +7231,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { "{{} }\n", // ); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); view.condition::(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) @@ -8989,31 +8932,13 @@ async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) { cx.executor().run_until_parked(); } - let buffer_1 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text_1.clone(), - ) - }); + let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx)); diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx); - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 1, - BufferId::new(cx.entity_id().as_u64() + 1).unwrap(), - sample_text_2.clone(), - ) - }); + let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx)); diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx); - let buffer_3 = cx.new_model(|cx| { - Buffer::new( - 2, - BufferId::new(cx.entity_id().as_u64() + 2).unwrap(), - sample_text_3.clone(), - ) - }); + let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx)); diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx); let multibuffer = cx.new_model(|cx| { @@ -9152,29 +9077,9 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) { "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}" ); - let buffer_1 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text_1.clone(), - ) - }); - - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 1, - BufferId::new(cx.entity_id().as_u64() + 1).unwrap(), - sample_text_2.clone(), - ) - }); - - let buffer_3 = cx.new_model(|cx| { - Buffer::new( - 2, - BufferId::new(cx.entity_id().as_u64() + 2).unwrap(), - sample_text_3.clone(), - ) - }); + let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx)); + let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx)); let multi_buffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 08aa6604c4..3758657c5f 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -570,7 +570,6 @@ mod tests { use language::Capability; use project::Project; use settings::SettingsStore; - use text::BufferId; use util::post_inc; #[gpui::test] @@ -870,13 +869,7 @@ mod tests { let font = font("Helvetica"); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "abc\ndefg\nhijkl\nmn", - ) - }); + let buffer = cx.new_model(|cx| Buffer::local("abc\ndefg\nhijkl\nmn", cx)); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite); multibuffer.push_excerpts( diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 9611a8581c..a2798da18f 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -21,7 +21,6 @@ use std::{ Arc, }, }; -use text::BufferId; use ui::Context; use util::{ assert_set_eq, @@ -76,10 +75,9 @@ impl EditorTestContext { ) -> EditorTestContext { let mut multibuffer = MultiBuffer::new(0, language::Capability::ReadWrite); let buffer = cx.new_model(|cx| { - for (i, excerpt) in excerpts.into_iter().enumerate() { + for excerpt in excerpts.into_iter() { let (text, ranges) = marked_text_ranges(excerpt, false); - let buffer = - cx.new_model(|_| Buffer::new(0, BufferId::new(i as u64 + 1).unwrap(), text)); + let buffer = cx.new_model(|cx| Buffer::local(text, cx)); multibuffer.push_excerpts( buffer, ranges.into_iter().map(|range| ExcerptRange { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 894b51e856..0aa160297a 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -32,9 +32,9 @@ use crate::{ AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, - PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, SharedString, - SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, - WindowAppearance, WindowContext, WindowHandle, WindowId, + PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation, + SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, + Window, WindowAppearance, WindowContext, WindowHandle, WindowId, }; mod async_context; @@ -1251,6 +1251,22 @@ impl Context for AppContext { }) } + fn reserve_model(&mut self) -> Self::Result> { + Reservation(self.entities.reserve()) + } + + fn insert_model( + &mut self, + reservation: Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result> { + self.update(|cx| { + let slot = reservation.0; + let entity = build_model(&mut ModelContext::new(cx, slot.downgrade())); + cx.entities.insert(slot, entity) + }) + } + /// Updates the entity referenced by the given model. The function is passed a mutable reference to the /// entity along with a `ModelContext` for the entity. fn update_model( @@ -1266,6 +1282,18 @@ impl Context for AppContext { }) } + fn read_model( + &self, + handle: &Model, + read: impl FnOnce(&T, &AppContext) -> R, + ) -> Self::Result + where + T: 'static, + { + let entity = self.entities.read(handle); + read(entity, self) + } + fn update_window(&mut self, handle: AnyWindowHandle, update: F) -> Result where F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, @@ -1295,18 +1323,6 @@ impl Context for AppContext { }) } - fn read_model( - &self, - handle: &Model, - read: impl FnOnce(&T, &AppContext) -> R, - ) -> Self::Result - where - T: 'static, - { - let entity = self.entities.read(handle); - read(entity, self) - } - fn read_window( &self, window: &WindowHandle, diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index 2835a2af98..de87e35fd0 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -1,7 +1,7 @@ use crate::{ AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context, - DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, Result, - Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, + DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, + Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, }; use anyhow::{anyhow, Context as _}; use derive_more::{Deref, DerefMut}; @@ -35,6 +35,28 @@ impl Context for AsyncAppContext { Ok(app.new_model(build_model)) } + fn reserve_model(&mut self) -> Result> { + let app = self + .app + .upgrade() + .ok_or_else(|| anyhow!("app was released"))?; + let mut app = app.borrow_mut(); + Ok(app.reserve_model()) + } + + fn insert_model( + &mut self, + reservation: Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Result> { + let app = self + .app + .upgrade() + .ok_or_else(|| anyhow!("app was released"))?; + let mut app = app.borrow_mut(); + Ok(app.insert_model(reservation, build_model)) + } + fn update_model( &mut self, handle: &Model, @@ -278,6 +300,19 @@ impl Context for AsyncWindowContext { self.window.update(self, |_, cx| cx.new_model(build_model)) } + fn reserve_model(&mut self) -> Result> { + self.window.update(self, |_, cx| cx.reserve_model()) + } + + fn insert_model( + &mut self, + reservation: Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result> { + self.window + .update(self, |_, cx| cx.insert_model(reservation, build_model)) + } + fn update_model( &mut self, handle: &Model, @@ -287,13 +322,6 @@ impl Context for AsyncWindowContext { .update(self, |_, cx| cx.update_model(handle, update)) } - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, - { - self.app.update_window(window, update) - } - fn read_model( &self, handle: &Model, @@ -305,6 +333,13 @@ impl Context for AsyncWindowContext { self.app.read_model(handle, read) } + fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result + where + F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, + { + self.app.update_window(window, update) + } + fn read_window( &self, window: &WindowHandle, diff --git a/crates/gpui/src/app/entity_map.rs b/crates/gpui/src/app/entity_map.rs index 2b04dec5e1..b5ef39eada 100644 --- a/crates/gpui/src/app/entity_map.rs +++ b/crates/gpui/src/app/entity_map.rs @@ -9,6 +9,7 @@ use std::{ hash::{Hash, Hasher}, marker::PhantomData, mem, + num::NonZeroU64, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, Weak, @@ -31,6 +32,11 @@ impl From for EntityId { } impl EntityId { + /// Converts this entity id to a [NonZeroU64] + pub fn as_non_zero_u64(self) -> NonZeroU64 { + NonZeroU64::new(self.0.as_ffi()).unwrap() + } + /// Converts this entity id to a [u64] pub fn as_u64(self) -> u64 { self.0.as_ffi() @@ -128,14 +134,16 @@ impl EntityMap { dropped_entity_ids .into_iter() - .map(|entity_id| { + .filter_map(|entity_id| { let count = ref_counts.counts.remove(entity_id).unwrap(); debug_assert_eq!( count.load(SeqCst), 0, "dropped an entity that was referenced" ); - (entity_id, self.entities.remove(entity_id).unwrap()) + // If the EntityId was allocated with `Context::reserve`, + // the entity may not have been inserted. + Some((entity_id, self.entities.remove(entity_id)?)) }) .collect() } diff --git a/crates/gpui/src/app/model_context.rs b/crates/gpui/src/app/model_context.rs index 0bdbf20988..3aebf88539 100644 --- a/crates/gpui/src/app/model_context.rs +++ b/crates/gpui/src/app/model_context.rs @@ -1,6 +1,7 @@ use crate::{ AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, - EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle, + EventEmitter, Model, Reservation, Subscription, Task, View, WeakModel, WindowContext, + WindowHandle, }; use anyhow::Result; use derive_more::{Deref, DerefMut}; @@ -229,6 +230,18 @@ impl<'a, T> Context for ModelContext<'a, T> { self.app.new_model(build_model) } + fn reserve_model(&mut self) -> Reservation { + self.app.reserve_model() + } + + fn insert_model( + &mut self, + reservation: Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U, + ) -> Self::Result> { + self.app.insert_model(reservation, build_model) + } + fn update_model( &mut self, handle: &Model, @@ -237,13 +250,6 @@ impl<'a, T> Context for ModelContext<'a, T> { self.app.update_model(handle, update) } - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut WindowContext<'_>) -> R, - { - self.app.update_window(window, update) - } - fn read_model( &self, handle: &Model, @@ -255,6 +261,13 @@ impl<'a, T> Context for ModelContext<'a, T> { self.app.read_model(handle, read) } + fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result + where + F: FnOnce(AnyView, &mut WindowContext<'_>) -> R, + { + self.app.update_window(window, update) + } + fn read_window( &self, window: &WindowHandle, diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 707bf91f75..b6bfd33f66 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -42,6 +42,20 @@ impl Context for TestAppContext { app.new_model(build_model) } + fn reserve_model(&mut self) -> Self::Result> { + let mut app = self.app.borrow_mut(); + app.reserve_model() + } + + fn insert_model( + &mut self, + reservation: crate::Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result> { + let mut app = self.app.borrow_mut(); + app.insert_model(reservation, build_model) + } + fn update_model( &mut self, handle: &Model, @@ -803,6 +817,18 @@ impl Context for VisualTestContext { self.cx.new_model(build_model) } + fn reserve_model(&mut self) -> Self::Result> { + self.cx.reserve_model() + } + + fn insert_model( + &mut self, + reservation: crate::Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result> { + self.cx.insert_model(reservation, build_model) + } + fn update_model( &mut self, handle: &Model, diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 1821c24401..9564d632d0 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -165,6 +165,19 @@ pub trait Context { build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, ) -> Self::Result>; + /// Reserve a slot for a model to be inserted later. + /// The returned [Reservation] allows you to obtain the [EntityId] for the future model. + fn reserve_model(&mut self) -> Self::Result>; + + /// Insert a new model in the app context based on a [Reservation] previously obtained from [`reserve_model`]. + /// + /// [`reserve_model`]: Self::reserve_model + fn insert_model( + &mut self, + reservation: Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result>; + /// Update a model in the app context. fn update_model( &mut self, @@ -198,6 +211,17 @@ pub trait Context { T: 'static; } +/// Returned by [Context::reserve_model] to later be passed to [Context::insert_model]. +/// Allows you to obtain the [EntityId] for a model before it is created. +pub struct Reservation(pub(crate) Slot); + +impl Reservation { + /// Returns the [EntityId] that will be associated with the model once it is inserted. + pub fn entity_id(&self) -> EntityId { + self.0.entity_id() + } +} + /// This trait is used for the different visual contexts in GPUI that /// require a window to be present. pub trait VisualContext: Context { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index c01c813e61..ee2b4fdeb1 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1830,6 +1830,18 @@ impl Context for WindowContext<'_> { self.entities.insert(slot, model) } + fn reserve_model(&mut self) -> Self::Result> { + self.app.reserve_model() + } + + fn insert_model( + &mut self, + reservation: crate::Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result> { + self.app.insert_model(reservation, build_model) + } + fn update_model( &mut self, model: &Model, @@ -1844,18 +1856,6 @@ impl Context for WindowContext<'_> { result } - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, - { - if window == self.window.handle { - let root_view = self.window.root_view.clone().unwrap(); - Ok(update(root_view, self)) - } else { - window.update(self.app, update) - } - } - fn read_model( &self, handle: &Model, @@ -1868,6 +1868,18 @@ impl Context for WindowContext<'_> { read(entity, &*self.app) } + fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result + where + F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, + { + if window == self.window.handle { + let root_view = self.window.root_view.clone().unwrap(); + Ok(update(root_view, self)) + } else { + window.update(self.app, update) + } + } + fn read_window( &self, window: &WindowHandle, @@ -2478,6 +2490,18 @@ impl Context for ViewContext<'_, V> { self.window_cx.new_model(build_model) } + fn reserve_model(&mut self) -> Self::Result> { + self.window_cx.reserve_model() + } + + fn insert_model( + &mut self, + reservation: crate::Reservation, + build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, + ) -> Self::Result> { + self.window_cx.insert_model(reservation, build_model) + } + fn update_model( &mut self, model: &Model, @@ -2486,13 +2510,6 @@ impl Context for ViewContext<'_, V> { self.window_cx.update_model(model, update) } - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, - { - self.window_cx.update_window(window, update) - } - fn read_model( &self, handle: &Model, @@ -2504,6 +2521,13 @@ impl Context for ViewContext<'_, V> { self.window_cx.read_model(handle, read) } + fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result + where + F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, + { + self.window_cx.update_window(window, update) + } + fn read_window( &self, window: &WindowHandle, diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index 2648c045b1..356a05a433 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -264,6 +264,18 @@ impl<'a> Context for ElementContext<'a> { self.cx.new_model(build_model) } + fn reserve_model(&mut self) -> Self::Result> { + self.cx.reserve_model() + } + + fn insert_model( + &mut self, + reservation: crate::Reservation, + build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T, + ) -> Self::Result> { + self.cx.insert_model(reservation, build_model) + } + fn update_model( &mut self, handle: &crate::Model, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 036e9964ff..7e89abd2fb 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -503,9 +503,9 @@ pub enum CharKind { impl Buffer { /// Create a new buffer with the given base text. - pub fn new>(replica_id: ReplicaId, id: BufferId, base_text: T) -> Self { + pub fn local>(base_text: T, cx: &mut ModelContext) -> Self { Self::build( - TextBuffer::new(replica_id, id, base_text.into()), + TextBuffer::new(0, cx.entity_id().as_non_zero_u64().into(), base_text.into()), None, None, Capability::ReadWrite, @@ -517,10 +517,10 @@ impl Buffer { remote_id: BufferId, replica_id: ReplicaId, capability: Capability, - base_text: String, + base_text: impl Into, ) -> Self { Self::build( - TextBuffer::new(replica_id, remote_id, base_text), + TextBuffer::new(replica_id, remote_id, base_text.into()), None, None, capability, diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index d0192be244..a1094a23be 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -44,12 +44,8 @@ fn test_line_endings(cx: &mut gpui::AppContext) { init_settings(cx, |_| {}); cx.new_model(|cx| { - let mut buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "one\r\ntwo\rthree", - ) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = + Buffer::local("one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx); assert_eq!(buffer.text(), "one\ntwo\nthree"); assert_eq!(buffer.line_ending(), LineEnding::Windows); @@ -256,10 +252,15 @@ fn test_edit_events(cx: &mut gpui::AppContext) { let buffer_1_events = Arc::new(Mutex::new(Vec::new())); let buffer_2_events = Arc::new(Mutex::new(Vec::new())); - let buffer1 = cx - .new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef")); - let buffer2 = cx - .new_model(|cx| Buffer::new(1, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef")); + let buffer1 = cx.new_model(|cx| Buffer::local("abcdef", cx)); + let buffer2 = cx.new_model(|cx| { + Buffer::remote( + BufferId::from(cx.entity_id().as_non_zero_u64()), + 1, + Capability::ReadWrite, + "abcdef", + ) + }); let buffer1_ops = Arc::new(Mutex::new(Vec::new())); buffer1.update(cx, { let buffer1_ops = buffer1_ops.clone(); @@ -338,8 +339,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) { #[gpui::test] async fn test_apply_diff(cx: &mut TestAppContext) { let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n"; - let buffer = - cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)); + let buffer = cx.new_model(|cx| Buffer::local(text, cx)); let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3))); let text = "a\nccc\ndddd\nffffff\n"; @@ -371,8 +371,7 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) { ] .join("\n"); - let buffer = - cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)); + let buffer = cx.new_model(|cx| Buffer::local(text, cx)); // Spawn a task to format the buffer's whitespace. // Pause so that the foratting task starts running. @@ -436,10 +435,8 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_reparse(cx: &mut gpui::TestAppContext) { let text = "fn a() {}"; - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); // Wait for the initial text to parse cx.executor().run_until_parked(); @@ -566,8 +563,7 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_resetting_language(cx: &mut gpui::TestAppContext) { let buffer = cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "{}") - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local("{}", cx).with_language(Arc::new(rust_lang()), cx); buffer.set_sync_parse_timeout(Duration::ZERO); buffer }); @@ -615,10 +611,8 @@ async fn test_outline(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); let outline = buffer .update(cx, |buffer, _| buffer.snapshot().outline(None)) .unwrap(); @@ -702,10 +696,8 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); let outline = buffer .update(cx, |buffer, _| buffer.snapshot().outline(None)) .unwrap(); @@ -741,10 +733,7 @@ async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(language), cx) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx)); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); // extra context nodes are included in the outline. @@ -786,10 +775,8 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx) - }); + let buffer = + cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx)); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); // point is at the start of an item @@ -1010,8 +997,7 @@ fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: & fn test_range_for_syntax_ancestor(cx: &mut AppContext) { cx.new_model(|cx| { let text = "fn a() { b(|c| {}) }"; - let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); let snapshot = buffer.snapshot(); assert_eq!( @@ -1051,8 +1037,7 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) { cx.new_model(|cx| { let text = "fn a() {}"; - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx); assert_eq!(buffer.text(), "fn a() {\n \n}"); @@ -1094,8 +1079,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) { cx.new_model(|cx| { let text = "fn a() {}"; - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx); assert_eq!(buffer.text(), "fn a() {\n\t\n}"); @@ -1134,9 +1118,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC init_settings(cx, |_| {}); cx.new_model(|cx| { - let mut buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), + let mut buffer = Buffer::local( " fn a() { c; @@ -1144,6 +1126,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC } " .unindent(), + cx, ) .with_language(Arc::new(rust_lang()), cx); @@ -1209,9 +1192,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC cx.new_model(|cx| { eprintln!("second buffer: {:?}", cx.entity_id()); - let mut buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), + let mut buffer = Buffer::local( " fn a() { b(); @@ -1219,6 +1200,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC " .replace('|', "") // marker to preserve trailing whitespace .unindent(), + cx, ) .with_language(Arc::new(rust_lang()), cx); @@ -1274,15 +1256,14 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap init_settings(cx, |_| {}); cx.new_model(|cx| { - let mut buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), + let mut buffer = Buffer::local( " fn a() { i } " .unindent(), + cx, ) .with_language(Arc::new(rust_lang()), cx); @@ -1336,13 +1317,12 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) { init_settings(cx, |_| {}); cx.new_model(|cx| { - let mut buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), + let mut buffer = Buffer::local( " fn a() {} " .unindent(), + cx, ) .with_language(Arc::new(rust_lang()), cx); @@ -1394,8 +1374,7 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) { cx.new_model(|cx| { let text = "a\nb"; - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); buffer.edit( [(0..1, "\n"), (2..3, "\n")], Some(AutoindentMode::EachLine), @@ -1421,8 +1400,7 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) { " .unindent(); - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); buffer.edit( [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")], Some(AutoindentMode::EachLine), @@ -1459,8 +1437,7 @@ fn test_autoindent_block_mode(cx: &mut AppContext) { } "# .unindent(); - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); // When this text was copied, both of the quotation marks were at the same // indent level, but the indentation of the first line was not included in @@ -1545,8 +1522,7 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex } "# .unindent(); - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); // The original indent columns are not known, so this text is // auto-indented in a block as if the first line was copied in @@ -1625,18 +1601,17 @@ fn test_autoindent_language_without_indents_query(cx: &mut AppContext) { " .unindent(); - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language( - Arc::new(Language::new( - LanguageConfig { - name: "Markdown".into(), - auto_indent_using_last_non_empty_line: false, - ..Default::default() - }, - Some(tree_sitter_json::language()), - )), - cx, - ); + let mut buffer = Buffer::local(text, cx).with_language( + Arc::new(Language::new( + LanguageConfig { + name: "Markdown".into(), + auto_indent_using_last_non_empty_line: false, + ..Default::default() + }, + Some(tree_sitter_json::language()), + )), + cx, + ); buffer.edit( [(Point::new(3, 0)..Point::new(3, 0), "\n")], Some(AutoindentMode::EachLine), @@ -1702,7 +1677,7 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) { false, ); - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text); + let mut buffer = Buffer::local(text, cx); buffer.set_language_registry(language_registry); buffer.set_language(Some(html_language), cx); buffer.edit( @@ -1738,8 +1713,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) { }); cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") - .with_language(Arc::new(ruby_lang()), cx); + let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx); let text = r#" class C @@ -1840,8 +1814,7 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) { "# .unindent(); - let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), &text) - .with_language(Arc::new(language), cx); + let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx); let snapshot = buffer.snapshot(); let config = snapshot.language_scope_at(0).unwrap(); @@ -1958,12 +1931,7 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) { "# .unindent(); - let buffer = Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - text.clone(), - ) - .with_language(Arc::new(language), cx); + let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx); let snapshot = buffer.snapshot(); // By default, all brackets are enabled @@ -2007,7 +1975,7 @@ fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) { language_registry.add(Arc::new(html_lang())); language_registry.add(Arc::new(erb_lang())); - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text); + let mut buffer = Buffer::local(text, cx); buffer.set_language_registry(language_registry.clone()); buffer.set_language( language_registry @@ -2042,7 +2010,7 @@ fn test_serialization(cx: &mut gpui::AppContext) { let mut now = Instant::now(); let buffer1 = cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abc"); + let mut buffer = Buffer::local("abc", cx); buffer.edit([(3..3, "D")], None, cx); now += Duration::from_secs(1); @@ -2097,13 +2065,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { let mut replica_ids = Vec::new(); let mut buffers = Vec::new(); let network = Arc::new(Mutex::new(Network::new(rng.clone()))); - let base_buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - base_text.as_str(), - ) - }); + let base_buffer = cx.new_model(|cx| Buffer::local(base_text.as_str(), cx)); for i in 0..rng.gen_range(min_peers..=max_peers) { let buffer = cx.new_model(|cx| { @@ -2623,12 +2585,7 @@ fn assert_bracket_pairs( ) { let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false); let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - expected_text.clone(), - ) - .with_language(Arc::new(language), cx) + Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx) }); let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot()); diff --git a/crates/languages/src/c.rs b/crates/languages/src/c.rs index 5cc1ea986b..1a6e8e113c 100644 --- a/crates/languages/src/c.rs +++ b/crates/languages/src/c.rs @@ -297,7 +297,6 @@ mod tests { use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; use settings::SettingsStore; use std::num::NonZeroU32; - use text::BufferId; #[gpui::test] async fn test_c_autoindent(cx: &mut TestAppContext) { @@ -315,8 +314,7 @@ mod tests { let language = crate::language("c", tree_sitter_c::language()); cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") - .with_language(language, cx); + let mut buffer = Buffer::local("", cx).with_language(language, cx); // empty function buffer.edit([(0..0, "int main() {}")], None, cx); diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index 970a223fc1..8faf512aed 100644 --- a/crates/languages/src/css.rs +++ b/crates/languages/src/css.rs @@ -136,7 +136,6 @@ async fn get_cached_server_binary( #[cfg(test)] mod tests { use gpui::{Context, TestAppContext}; - use text::BufferId; use unindent::Unindent; #[gpui::test] @@ -168,10 +167,8 @@ mod tests { "# .unindent(); - let buffer = cx.new_model(|cx| { - language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = + cx.new_model(|cx| language::Buffer::local(text, cx).with_language(language, cx)); let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); assert_eq!( outline diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index fea4179627..560385082e 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -186,7 +186,6 @@ mod tests { use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; use settings::SettingsStore; use std::num::NonZeroU32; - use text::BufferId; #[gpui::test] async fn test_python_autoindent(cx: &mut TestAppContext) { @@ -204,8 +203,7 @@ mod tests { }); cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") - .with_language(language, cx); + let mut buffer = Buffer::local("", cx).with_language(language, cx); let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext| { let ix = buffer.len(); buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx); diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index b5c710dc5c..e090819268 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -465,7 +465,6 @@ mod tests { use gpui::{BorrowAppContext, Context, Hsla, TestAppContext}; use language::language_settings::AllLanguageSettings; use settings::SettingsStore; - use text::BufferId; use theme::SyntaxTheme; #[gpui::test] @@ -682,8 +681,7 @@ mod tests { let language = crate::language("rust", tree_sitter_rust::language()); cx.new_model(|cx| { - let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") - .with_language(language, cx); + let mut buffer = Buffer::local("", cx).with_language(language, cx); // indent between braces buffer.set_text("fn a() {}", cx); diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index dfb9bc690e..13ff23f26d 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -413,7 +413,6 @@ async fn get_cached_eslint_server_binary( #[cfg(test)] mod tests { use gpui::{Context, TestAppContext}; - use text::BufferId; use unindent::Unindent; #[gpui::test] @@ -435,10 +434,8 @@ mod tests { "# .unindent(); - let buffer = cx.new_model(|cx| { - language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - .with_language(language, cx) - }); + let buffer = + cx.new_model(|cx| language::Buffer::local(text, cx).with_language(language, cx)); let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); assert_eq!( outline diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 2152269dd3..bdfac66ced 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -1700,8 +1700,7 @@ impl MultiBuffer { #[cfg(any(test, feature = "test-support"))] impl MultiBuffer { pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model { - let buffer = cx - .new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)); + let buffer = cx.new_model(|cx| Buffer::local(text, cx)); cx.new_model(|cx| Self::singleton(buffer, cx)) } @@ -1711,9 +1710,7 @@ impl MultiBuffer { ) -> Model { let multi = cx.new_model(|_| Self::new(0, Capability::ReadWrite)); for (text, ranges) in excerpts { - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - }); + let buffer = cx.new_model(|cx| Buffer::local(text, cx)); let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange { context: range, primary: None, @@ -1802,9 +1799,7 @@ impl MultiBuffer { if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) { let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() { let text = RandomCharIter::new(&mut *rng).take(10).collect::(); - buffers.push(cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) - })); + buffers.push(cx.new_model(|cx| Buffer::local(text, cx))); let buffer = buffers.last().unwrap().read(cx); log::info!( "Creating new buffer {} with text: {:?}", @@ -4327,13 +4322,7 @@ mod tests { #[gpui::test] fn test_singleton(cx: &mut AppContext) { - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(6, 6, 'a'), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let snapshot = multibuffer.read(cx).snapshot(cx); @@ -4360,8 +4349,7 @@ mod tests { #[gpui::test] fn test_remote(cx: &mut AppContext) { - let host_buffer = - cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "a")); + let host_buffer = cx.new_model(|cx| Buffer::local("a", cx)); let guest_buffer = cx.new_model(|cx| { let state = host_buffer.read(cx).to_proto(); let ops = cx @@ -4392,20 +4380,8 @@ mod tests { #[gpui::test] fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) { - let buffer_1 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(6, 6, 'a'), - ) - }); - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(6, 6, 'g'), - ) - }); + let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let events = Arc::new(RwLock::new(Vec::::new())); @@ -4638,20 +4614,8 @@ mod tests { #[gpui::test] fn test_excerpt_events(cx: &mut AppContext) { - let buffer_1 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(10, 3, 'a'), - ) - }); - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(10, 3, 'm'), - ) - }); + let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'a'), cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'm'), cx)); let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); @@ -4756,13 +4720,7 @@ mod tests { #[gpui::test] fn test_push_excerpts_with_context_lines(cx: &mut AppContext) { - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(20, 3, 'a'), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { multibuffer.push_excerpts_with_context_lines( @@ -4798,13 +4756,7 @@ mod tests { #[gpui::test] async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) { - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - sample_text(20, 3, 'a'), - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { let snapshot = buffer.read(cx); @@ -4850,9 +4802,7 @@ mod tests { #[gpui::test] fn test_singleton_multibuffer_anchors(cx: &mut AppContext) { - let buffer = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") - }); + let buffer = cx.new_model(|cx| Buffer::local("abcd", cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let old_snapshot = multibuffer.read(cx).snapshot(cx); buffer.update(cx, |buffer, cx| { @@ -4872,12 +4822,8 @@ mod tests { #[gpui::test] fn test_multibuffer_anchors(cx: &mut AppContext) { - let buffer_1 = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") - }); - let buffer_2 = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "efghi") - }); + let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local("efghi", cx)); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite); multibuffer.push_excerpts( @@ -4934,16 +4880,8 @@ mod tests { #[gpui::test] fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) { - let buffer_1 = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") - }); - let buffer_2 = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - "ABCDEFGHIJKLMNOP", - ) - }); + let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local("ABCDEFGHIJKLMNOP", cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); // Create an insertion id in buffer 1 that doesn't exist in buffer 2. @@ -5154,13 +5092,7 @@ mod tests { let base_text = util::RandomCharIter::new(&mut rng) .take(10) .collect::(); - buffers.push(cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - base_text, - ) - })); + buffers.push(cx.new_model(|cx| Buffer::local(base_text, cx))); buffers.last().unwrap() } else { buffers.choose(&mut rng).unwrap() @@ -5502,12 +5434,8 @@ mod tests { let test_settings = SettingsStore::test(cx); cx.set_global(test_settings); - let buffer_1 = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "1234") - }); - let buffer_2 = cx.new_model(|cx| { - Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "5678") - }); + let buffer_1 = cx.new_model(|cx| Buffer::local("1234", cx)); + let buffer_2 = cx.new_model(|cx| Buffer::local("5678", cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let group_interval = multibuffer.read(cx).history.group_interval; multibuffer.update(cx, |multibuffer, cx| { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 58c1a6a093..12aa405a00 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -178,7 +178,6 @@ pub struct Project { collaborators: HashMap, client_subscriptions: Vec, _subscriptions: Vec, - next_buffer_id: BufferId, loading_buffers: HashMap, anyhow::Error>>>>, incomplete_remote_buffers: HashMap>, shared_buffers: HashMap>, @@ -675,7 +674,6 @@ impl Project { flush_language_server_update: None, pending_language_server_update: None, collaborators: Default::default(), - next_buffer_id: BufferId::new(1).unwrap(), opened_buffers: Default::default(), shared_buffers: Default::default(), loading_buffers_by_path: Default::default(), @@ -792,7 +790,6 @@ impl Project { pending_language_server_update: None, flush_language_server_update: None, loading_buffers_by_path: Default::default(), - next_buffer_id: BufferId::new(1).unwrap(), loading_buffers: Default::default(), shared_buffers: Default::default(), incomplete_remote_buffers: Default::default(), @@ -1849,9 +1846,8 @@ impl Project { if self.is_remote() { return Err(anyhow!("creating buffers as a guest is not supported yet")); } - let id = self.next_buffer_id.next(); let buffer = cx.new_model(|cx| { - Buffer::new(self.replica_id(), id, text) + Buffer::local(text, cx) .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx) }); self.register_buffer(&buffer, cx)?; @@ -1950,10 +1946,9 @@ impl Project { worktree: Model, cx: &mut ModelContext, ) -> Task>> { - let buffer_id = self.next_buffer_id.next(); let load_buffer = worktree.update(cx, |worktree, cx| { let worktree = worktree.as_local_mut().unwrap(); - worktree.load_buffer(buffer_id, &path, cx) + worktree.load_buffer(&path, cx) }); fn is_not_found_error(error: &anyhow::Error) -> bool { error @@ -1967,7 +1962,7 @@ impl Project { Err(error) if is_not_found_error(&error) => { worktree.update(&mut cx, |worktree, cx| { let worktree = worktree.as_local_mut().unwrap(); - worktree.new_buffer(buffer_id, path, cx) + worktree.new_buffer(path, cx) }) } Err(e) => Err(e), diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 0d3d199ce1..5a1d4bafaa 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1141,7 +1141,7 @@ mod tests { use super::*; use editor::{DisplayPoint, Editor}; use gpui::{Context, Hsla, TestAppContext, VisualTestContext}; - use language::{Buffer, BufferId}; + use language::Buffer; use smol::stream::StreamExt as _; use unindent::Unindent as _; @@ -1161,9 +1161,7 @@ mod tests { ) -> (View, View, &mut VisualTestContext) { init_globals(cx); let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), + Buffer::local( r#" A regular expression (shortened as regex or regexp;[1] also referred to as rational expression[2][3]) is a sequence of characters that specifies a search @@ -1171,6 +1169,7 @@ mod tests { for "find" or "find and replace" operations on strings, or for input validation. "# .unindent(), + cx, ) }); let cx = cx.add_empty_window(); @@ -1519,13 +1518,7 @@ mod tests { expected_query_matches_count > 1, "Should pick a query with multiple results" ); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - buffer_text, - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(buffer_text, cx)); let window = cx.add_window(|_| gpui::Empty); let editor = window.build_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); @@ -1721,13 +1714,7 @@ mod tests { for "find" or "find and replace" operations on strings, or for input validation. "# .unindent(); - let buffer = cx.new_model(|cx| { - Buffer::new( - 0, - BufferId::new(cx.entity_id().as_u64()).unwrap(), - buffer_text, - ) - }); + let buffer = cx.new_model(|cx| Buffer::local(buffer_text, cx)); let cx = cx.add_empty_window(); let editor = cx.new_view(|cx| Editor::for_buffer(buffer.clone(), None, cx)); diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 55b6e9eefc..4c780b3a2b 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -71,12 +71,19 @@ impl Display for BufferId { } } +impl From for BufferId { + fn from(id: NonZeroU64) -> Self { + BufferId(id) + } +} + impl BufferId { /// Returns Err if `id` is outside of BufferId domain. pub fn new(id: u64) -> anyhow::Result { let id = NonZeroU64::new(id).context("Buffer id cannot be 0.")?; Ok(Self(id)) } + /// Increments this buffer id, returning the old value. /// So that's a post-increment operator in disguise. pub fn next(&mut self) -> Self { diff --git a/crates/vim/src/editor_events.rs b/crates/vim/src/editor_events.rs index d4ba0e5dbc..3fccf2eba1 100644 --- a/crates/vim/src/editor_events.rs +++ b/crates/vim/src/editor_events.rs @@ -73,14 +73,14 @@ mod test { use crate::{test::VimTestContext, Vim}; use editor::Editor; use gpui::{Context, Entity, VisualTestContext}; - use language::{Buffer, BufferId}; + use language::Buffer; // regression test for blur called with a different active editor #[gpui::test] async fn test_blur_focus(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; - let buffer = cx.new_model(|_| Buffer::new(0, BufferId::new(1).unwrap(), "a = 1\nb = 2\n")); + let buffer = cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx)); let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx)); let editor2 = cx .update(|cx| { @@ -115,7 +115,7 @@ mod test { let mut cx1 = VisualTestContext::from_window(cx.window, &cx); let editor1 = cx.editor.clone(); - let buffer = cx.new_model(|_| Buffer::new(0, BufferId::new(1).unwrap(), "a = 1\nb = 2\n")); + let buffer = cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx)); let (editor2, cx2) = cx.add_window_view(|cx| Editor::for_buffer(buffer, None, cx)); editor2.update(cx2, |_, cx| { diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 82abf17bb4..24e4f7c64f 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -751,20 +751,21 @@ impl LocalWorktree { pub fn load_buffer( &mut self, - id: BufferId, path: &Path, cx: &mut ModelContext, ) -> Task>> { let path = Arc::from(path); + let reservation = cx.reserve_model(); + let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64()); cx.spawn(move |this, mut cx| async move { let (file, contents, diff_base) = this .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))? .await?; let text_buffer = cx .background_executor() - .spawn(async move { text::Buffer::new(0, id, contents) }) + .spawn(async move { text::Buffer::new(0, buffer_id, contents) }) .await; - cx.new_model(|_| { + cx.insert_model(reservation, |_| { Buffer::build( text_buffer, diff_base, @@ -777,13 +778,13 @@ impl LocalWorktree { pub fn new_buffer( &mut self, - buffer_id: BufferId, path: Arc, cx: &mut ModelContext, ) -> Model { - let text_buffer = text::Buffer::new(0, buffer_id, "".into()); let worktree = cx.handle(); - cx.new_model(|_| { + cx.new_model(|cx| { + let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64()); + let text_buffer = text::Buffer::new(0, buffer_id, "".into()); Buffer::build( text_buffer, None, diff --git a/crates/worktree/src/worktree_tests.rs b/crates/worktree/src/worktree_tests.rs index 70facfe150..5efee216b6 100644 --- a/crates/worktree/src/worktree_tests.rs +++ b/crates/worktree/src/worktree_tests.rs @@ -21,7 +21,6 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use text::BufferId; use util::{http::FakeHttpClient, test::temp_tree, ResultExt}; #[gpui::test] @@ -577,11 +576,9 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) { let prev_read_dir_count = fs.read_dir_call_count(); let buffer = tree .update(cx, |tree, cx| { - tree.as_local_mut().unwrap().load_buffer( - BufferId::new(1).unwrap(), - "one/node_modules/b/b1.js".as_ref(), - cx, - ) + tree.as_local_mut() + .unwrap() + .load_buffer("one/node_modules/b/b1.js".as_ref(), cx) }) .await .unwrap(); @@ -621,11 +618,9 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) { let prev_read_dir_count = fs.read_dir_call_count(); let buffer = tree .update(cx, |tree, cx| { - tree.as_local_mut().unwrap().load_buffer( - BufferId::new(1).unwrap(), - "one/node_modules/a/a2.js".as_ref(), - cx, - ) + tree.as_local_mut() + .unwrap() + .load_buffer("one/node_modules/a/a2.js".as_ref(), cx) }) .await .unwrap();