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>
This commit is contained in:
Nathan Sobo 2024-04-10 07:32:51 -07:00 committed by GitHub
parent 664efef76b
commit 7abb63cfda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 376 additions and 519 deletions

View File

@ -31,7 +31,7 @@ use gpui::{
StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle,
View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext, 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 parking_lot::Mutex;
use project::Project; use project::Project;
use search::{buffer_search::DivRegistrar, BufferSearchBar}; use search::{buffer_search::DivRegistrar, BufferSearchBar};
@ -1310,7 +1310,7 @@ impl Conversation {
) -> Self { ) -> Self {
let markdown = language_registry.language_for_name("Markdown"); let markdown = language_registry.language_for_name("Markdown");
let buffer = cx.new_model(|cx| { 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); buffer.set_language_registry(language_registry);
cx.spawn(|buffer, mut cx| async move { cx.spawn(|buffer, mut cx| async move {
let markdown = markdown.await?; let markdown = markdown.await?;
@ -1398,11 +1398,7 @@ impl Conversation {
let mut message_anchors = Vec::new(); let mut message_anchors = Vec::new();
let mut next_message_id = MessageId(0); let mut next_message_id = MessageId(0);
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| {
let mut buffer = Buffer::new( let mut buffer = Buffer::local(saved_conversation.text, cx);
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
saved_conversation.text,
);
for message in saved_conversation.messages { for message in saved_conversation.messages {
message_anchors.push(MessageAnchor { message_anchors.push(MessageAnchor {
id: message.id, id: message.id,

View File

@ -361,8 +361,8 @@ mod tests {
use gpui::{Context, TestAppContext}; use gpui::{Context, TestAppContext};
use indoc::indoc; use indoc::indoc;
use language::{ use language::{
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
LanguageMatcher, Point, Point,
}; };
use rand::prelude::*; use rand::prelude::*;
use serde::Serialize; use serde::Serialize;
@ -388,9 +388,8 @@ mod tests {
} }
} }
"}; "};
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) 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 buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let range = buffer.read_with(cx, |buffer, cx| { let range = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx); let snapshot = buffer.snapshot(cx);
@ -447,9 +446,8 @@ mod tests {
le le
} }
"}; "};
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) 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 buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let position = buffer.read_with(cx, |buffer, cx| { let position = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx); let snapshot = buffer.snapshot(cx);
@ -506,9 +504,8 @@ mod tests {
" \n", " \n",
"}\n" // "}\n" //
); );
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) 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 buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let position = buffer.read_with(cx, |buffer, cx| { let position = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx); let snapshot = buffer.snapshot(cx);

View File

@ -1042,9 +1042,7 @@ mod tests {
async fn test_buffer_management(cx: &mut TestAppContext) { async fn test_buffer_management(cx: &mut TestAppContext) {
let (copilot, mut lsp) = Copilot::fake(cx); let (copilot, mut lsp) = Copilot::fake(cx);
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local("Hello", cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "Hello")
});
let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64()) let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64())
.parse() .parse()
.unwrap(); .unwrap();
@ -1062,13 +1060,7 @@ mod tests {
} }
); );
let buffer_2 = cx.new_model(|cx| { let buffer_2 = cx.new_model(|cx| Buffer::local("Goodbye", cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"Goodbye",
)
});
let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64()) let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64())
.parse() .parse()
.unwrap(); .unwrap();

View File

@ -272,7 +272,7 @@ mod tests {
use indoc::indoc; use indoc::indoc;
use language::{ use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent}, language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
BufferId, Point, Point,
}; };
use project::Project; use project::Project;
use serde_json::json; use serde_json::json;
@ -729,20 +729,8 @@ mod tests {
let (copilot, copilot_lsp) = Copilot::fake(cx); let (copilot, copilot_lsp) = Copilot::fake(cx);
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx));
Buffer::new( let buffer_2 = cx.new_model(|cx| Buffer::local("c = 3\nd = 4\n", cx));
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 multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, language::Capability::ReadWrite); let mut multibuffer = MultiBuffer::new(0, language::Capability::ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(

View File

@ -984,7 +984,6 @@ pub mod tests {
use settings::SettingsStore; use settings::SettingsStore;
use smol::stream::StreamExt; use smol::stream::StreamExt;
use std::{env, sync::Arc}; use std::{env, sync::Arc};
use text::BufferId;
use theme::{LoadThemes, SyntaxTheme}; use theme::{LoadThemes, SyntaxTheme};
use util::test::{marked_text_ranges, sample_text}; use util::test::{marked_text_ranges, sample_text};
use Bias::*; 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()))); cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
@ -1533,10 +1529,7 @@ pub mod tests {
cx.update(|cx| init_test(cx, |_| {})); cx.update(|cx| init_test(cx, |_| {}));
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); 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 (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));

View File

@ -1328,37 +1328,19 @@ impl InlayHintRefreshReason {
impl Editor { impl Editor {
pub fn single_line(cx: &mut ViewContext<Self>) -> Self { pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local("", cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
String::new(),
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
Self::new(EditorMode::SingleLine, buffer, None, cx) Self::new(EditorMode::SingleLine, buffer, None, cx)
} }
pub fn multi_line(cx: &mut ViewContext<Self>) -> Self { pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local("", cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
String::new(),
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
Self::new(EditorMode::Full, buffer, None, cx) Self::new(EditorMode::Full, buffer, None, cx)
} }
pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self { pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local("", cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
String::new(),
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx) Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
} }

View File

@ -38,8 +38,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| {
let mut buffer = let mut buffer = language::Buffer::local("123456", cx);
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456");
buffer.set_group_interval(Duration::from_secs(1)); buffer.set_group_interval(Duration::from_secs(1));
buffer buffer
}); });
@ -154,9 +153,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let mut now = Instant::now(); let mut now = Instant::now();
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| language::Buffer::local("123456", cx));
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456")
});
let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval()); let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer.clone(), 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, |_| {}); init_test(cx, |_| {});
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| {
let mut buffer = let mut buffer = language::Buffer::local("abcde", cx);
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcde");
// Ensure automatic grouping doesn't occur. // Ensure automatic grouping doesn't occur.
buffer.set_group_interval(Duration::ZERO); buffer.set_group_interval(Duration::ZERO);
buffer buffer
@ -2344,21 +2340,10 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
None, None,
)); ));
let toml_buffer = cx.new_model(|cx| { let toml_buffer =
Buffer::new( cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"a = 1\nb = 2\n",
)
.with_language(toml_language, cx)
});
let rust_buffer = cx.new_model(|cx| { let rust_buffer = cx.new_model(|cx| {
Buffer::new( Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx)
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"const c: usize = 3;\n",
)
.with_language(rust_language, cx)
}); });
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
@ -4304,10 +4289,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (view, cx) = cx.add_window_view(|cx| build_editor(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 text = "fn a() {}";
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
editor editor
@ -5129,10 +5108,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) view.condition::<crate::EditorEvent>(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(); .unindent();
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
editor editor
@ -5470,10 +5443,7 @@ async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
Some(tree_sitter_rust::language()), Some(tree_sitter_rust::language()),
)); ));
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local("", cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "")
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
editor editor
@ -6939,13 +6909,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(3, 4, 'a'),
)
});
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
@ -7029,13 +6993,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
primary: None, primary: None,
} }
}); });
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(initial_text, cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
initial_text,
)
});
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(buffer, excerpt_ranges, cx); 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) { fn test_refresh_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(3, 4, 'a'),
)
});
let mut excerpt1_id = None; let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite); 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) { fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(3, 4, 'a'),
)
});
let mut excerpt1_id = None; let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
@ -7285,10 +7231,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
"{{} }\n", // "{{} }\n", //
); );
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(language, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) view.condition::<crate::EditorEvent>(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(); cx.executor().run_until_parked();
} }
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text_1.clone(),
)
});
diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx); diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
let buffer_2 = cx.new_model(|cx| { let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
Buffer::new(
1,
BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
sample_text_2.clone(),
)
});
diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx); diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
let buffer_3 = cx.new_model(|cx| { let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
Buffer::new(
2,
BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
sample_text_3.clone(),
)
});
diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx); diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
let multibuffer = cx.new_model(|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}" "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| { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text_1.clone(), cx));
Buffer::new( let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text_2.clone(), cx));
0, let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text_3.clone(), cx));
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 multi_buffer = cx.new_model(|cx| { let multi_buffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite); let mut multibuffer = MultiBuffer::new(0, ReadWrite);

View File

@ -570,7 +570,6 @@ mod tests {
use language::Capability; use language::Capability;
use project::Project; use project::Project;
use settings::SettingsStore; use settings::SettingsStore;
use text::BufferId;
use util::post_inc; use util::post_inc;
#[gpui::test] #[gpui::test]
@ -870,13 +869,7 @@ mod tests {
let font = font("Helvetica"); let font = font("Helvetica");
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local("abc\ndefg\nhijkl\nmn", cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"abc\ndefg\nhijkl\nmn",
)
});
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite); let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(

View File

@ -21,7 +21,6 @@ use std::{
Arc, Arc,
}, },
}; };
use text::BufferId;
use ui::Context; use ui::Context;
use util::{ use util::{
assert_set_eq, assert_set_eq,
@ -76,10 +75,9 @@ impl EditorTestContext {
) -> EditorTestContext { ) -> EditorTestContext {
let mut multibuffer = MultiBuffer::new(0, language::Capability::ReadWrite); let mut multibuffer = MultiBuffer::new(0, language::Capability::ReadWrite);
let buffer = cx.new_model(|cx| { 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 (text, ranges) = marked_text_ranges(excerpt, false);
let buffer = let buffer = cx.new_model(|cx| Buffer::local(text, cx));
cx.new_model(|_| Buffer::new(0, BufferId::new(i as u64 + 1).unwrap(), text));
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer, buffer,
ranges.into_iter().map(|range| ExcerptRange { ranges.into_iter().map(|range| ExcerptRange {

View File

@ -32,9 +32,9 @@ use crate::{
AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context, AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context,
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point,
PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, SharedString, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext,
WindowAppearance, WindowContext, WindowHandle, WindowId, Window, WindowAppearance, WindowContext, WindowHandle, WindowId,
}; };
mod async_context; mod async_context;
@ -1251,6 +1251,22 @@ impl Context for AppContext {
}) })
} }
fn reserve_model<T: 'static>(&mut self) -> Self::Result<Reservation<T>> {
Reservation(self.entities.reserve())
}
fn insert_model<T: 'static>(
&mut self,
reservation: Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
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 /// 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. /// entity along with a `ModelContext` for the entity.
fn update_model<T: 'static, R>( fn update_model<T: 'static, R>(
@ -1266,6 +1282,18 @@ impl Context for AppContext {
}) })
} }
fn read_model<T, R>(
&self,
handle: &Model<T>,
read: impl FnOnce(&T, &AppContext) -> R,
) -> Self::Result<R>
where
T: 'static,
{
let entity = self.entities.read(handle);
read(entity, self)
}
fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T> fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
where where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
@ -1295,18 +1323,6 @@ impl Context for AppContext {
}) })
} }
fn read_model<T, R>(
&self,
handle: &Model<T>,
read: impl FnOnce(&T, &AppContext) -> R,
) -> Self::Result<R>
where
T: 'static,
{
let entity = self.entities.read(handle);
read(entity, self)
}
fn read_window<T, R>( fn read_window<T, R>(
&self, &self,
window: &WindowHandle<T>, window: &WindowHandle<T>,

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context, AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context,
DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, Result, DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render,
Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle,
}; };
use anyhow::{anyhow, Context as _}; use anyhow::{anyhow, Context as _};
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -35,6 +35,28 @@ impl Context for AsyncAppContext {
Ok(app.new_model(build_model)) Ok(app.new_model(build_model))
} }
fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
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<T: 'static>(
&mut self,
reservation: Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Result<Model<T>> {
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<T: 'static, R>( fn update_model<T: 'static, R>(
&mut self, &mut self,
handle: &Model<T>, handle: &Model<T>,
@ -278,6 +300,19 @@ impl Context for AsyncWindowContext {
self.window.update(self, |_, cx| cx.new_model(build_model)) self.window.update(self, |_, cx| cx.new_model(build_model))
} }
fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
self.window.update(self, |_, cx| cx.reserve_model())
}
fn insert_model<T: 'static>(
&mut self,
reservation: Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
self.window
.update(self, |_, cx| cx.insert_model(reservation, build_model))
}
fn update_model<T: 'static, R>( fn update_model<T: 'static, R>(
&mut self, &mut self,
handle: &Model<T>, handle: &Model<T>,
@ -287,13 +322,6 @@ impl Context for AsyncWindowContext {
.update(self, |_, cx| cx.update_model(handle, update)) .update(self, |_, cx| cx.update_model(handle, update))
} }
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
{
self.app.update_window(window, update)
}
fn read_model<T, R>( fn read_model<T, R>(
&self, &self,
handle: &Model<T>, handle: &Model<T>,
@ -305,6 +333,13 @@ impl Context for AsyncWindowContext {
self.app.read_model(handle, read) self.app.read_model(handle, read)
} }
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
{
self.app.update_window(window, update)
}
fn read_window<T, R>( fn read_window<T, R>(
&self, &self,
window: &WindowHandle<T>, window: &WindowHandle<T>,

View File

@ -9,6 +9,7 @@ use std::{
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData, marker::PhantomData,
mem, mem,
num::NonZeroU64,
sync::{ sync::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak, Arc, Weak,
@ -31,6 +32,11 @@ impl From<u64> for EntityId {
} }
impl 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] /// Converts this entity id to a [u64]
pub fn as_u64(self) -> u64 { pub fn as_u64(self) -> u64 {
self.0.as_ffi() self.0.as_ffi()
@ -128,14 +134,16 @@ impl EntityMap {
dropped_entity_ids dropped_entity_ids
.into_iter() .into_iter()
.map(|entity_id| { .filter_map(|entity_id| {
let count = ref_counts.counts.remove(entity_id).unwrap(); let count = ref_counts.counts.remove(entity_id).unwrap();
debug_assert_eq!( debug_assert_eq!(
count.load(SeqCst), count.load(SeqCst),
0, 0,
"dropped an entity that was referenced" "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() .collect()
} }

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, 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 anyhow::Result;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -229,6 +230,18 @@ impl<'a, T> Context for ModelContext<'a, T> {
self.app.new_model(build_model) self.app.new_model(build_model)
} }
fn reserve_model<U: 'static>(&mut self) -> Reservation<U> {
self.app.reserve_model()
}
fn insert_model<U: 'static>(
&mut self,
reservation: Reservation<U>,
build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U,
) -> Self::Result<Model<U>> {
self.app.insert_model(reservation, build_model)
}
fn update_model<U: 'static, R>( fn update_model<U: 'static, R>(
&mut self, &mut self,
handle: &Model<U>, handle: &Model<U>,
@ -237,13 +250,6 @@ impl<'a, T> Context for ModelContext<'a, T> {
self.app.update_model(handle, update) self.app.update_model(handle, update)
} }
fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> R,
{
self.app.update_window(window, update)
}
fn read_model<U, R>( fn read_model<U, R>(
&self, &self,
handle: &Model<U>, handle: &Model<U>,
@ -255,6 +261,13 @@ impl<'a, T> Context for ModelContext<'a, T> {
self.app.read_model(handle, read) self.app.read_model(handle, read)
} }
fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> R,
{
self.app.update_window(window, update)
}
fn read_window<U, R>( fn read_window<U, R>(
&self, &self,
window: &WindowHandle<U>, window: &WindowHandle<U>,

View File

@ -42,6 +42,20 @@ impl Context for TestAppContext {
app.new_model(build_model) app.new_model(build_model)
} }
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
let mut app = self.app.borrow_mut();
app.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
let mut app = self.app.borrow_mut();
app.insert_model(reservation, build_model)
}
fn update_model<T: 'static, R>( fn update_model<T: 'static, R>(
&mut self, &mut self,
handle: &Model<T>, handle: &Model<T>,
@ -803,6 +817,18 @@ impl Context for VisualTestContext {
self.cx.new_model(build_model) self.cx.new_model(build_model)
} }
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
self.cx.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
self.cx.insert_model(reservation, build_model)
}
fn update_model<T, R>( fn update_model<T, R>(
&mut self, &mut self,
handle: &Model<T>, handle: &Model<T>,

View File

@ -165,6 +165,19 @@ pub trait Context {
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>>; ) -> Self::Result<Model<T>>;
/// 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<T: 'static>(&mut self) -> Self::Result<Reservation<T>>;
/// 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<T: 'static>(
&mut self,
reservation: Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>>;
/// Update a model in the app context. /// Update a model in the app context.
fn update_model<T, R>( fn update_model<T, R>(
&mut self, &mut self,
@ -198,6 +211,17 @@ pub trait Context {
T: 'static; 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<T>(pub(crate) Slot<T>);
impl<T: 'static> Reservation<T> {
/// 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 /// This trait is used for the different visual contexts in GPUI that
/// require a window to be present. /// require a window to be present.
pub trait VisualContext: Context { pub trait VisualContext: Context {

View File

@ -1830,6 +1830,18 @@ impl Context for WindowContext<'_> {
self.entities.insert(slot, model) self.entities.insert(slot, model)
} }
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
self.app.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
self.app.insert_model(reservation, build_model)
}
fn update_model<T: 'static, R>( fn update_model<T: 'static, R>(
&mut self, &mut self,
model: &Model<T>, model: &Model<T>,
@ -1844,18 +1856,6 @@ impl Context for WindowContext<'_> {
result result
} }
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
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<T, R>( fn read_model<T, R>(
&self, &self,
handle: &Model<T>, handle: &Model<T>,
@ -1868,6 +1868,18 @@ impl Context for WindowContext<'_> {
read(entity, &*self.app) read(entity, &*self.app)
} }
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
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<T, R>( fn read_window<T, R>(
&self, &self,
window: &WindowHandle<T>, window: &WindowHandle<T>,
@ -2478,6 +2490,18 @@ impl<V> Context for ViewContext<'_, V> {
self.window_cx.new_model(build_model) self.window_cx.new_model(build_model)
} }
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
self.window_cx.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
self.window_cx.insert_model(reservation, build_model)
}
fn update_model<T: 'static, R>( fn update_model<T: 'static, R>(
&mut self, &mut self,
model: &Model<T>, model: &Model<T>,
@ -2486,13 +2510,6 @@ impl<V> Context for ViewContext<'_, V> {
self.window_cx.update_model(model, update) self.window_cx.update_model(model, update)
} }
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
{
self.window_cx.update_window(window, update)
}
fn read_model<T, R>( fn read_model<T, R>(
&self, &self,
handle: &Model<T>, handle: &Model<T>,
@ -2504,6 +2521,13 @@ impl<V> Context for ViewContext<'_, V> {
self.window_cx.read_model(handle, read) self.window_cx.read_model(handle, read)
} }
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
{
self.window_cx.update_window(window, update)
}
fn read_window<T, R>( fn read_window<T, R>(
&self, &self,
window: &WindowHandle<T>, window: &WindowHandle<T>,

View File

@ -264,6 +264,18 @@ impl<'a> Context for ElementContext<'a> {
self.cx.new_model(build_model) self.cx.new_model(build_model)
} }
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
self.cx.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
) -> Self::Result<crate::Model<T>> {
self.cx.insert_model(reservation, build_model)
}
fn update_model<T, R>( fn update_model<T, R>(
&mut self, &mut self,
handle: &crate::Model<T>, handle: &crate::Model<T>,

View File

@ -503,9 +503,9 @@ pub enum CharKind {
impl Buffer { impl Buffer {
/// Create a new buffer with the given base text. /// Create a new buffer with the given base text.
pub fn new<T: Into<String>>(replica_id: ReplicaId, id: BufferId, base_text: T) -> Self { pub fn local<T: Into<String>>(base_text: T, cx: &mut ModelContext<Self>) -> Self {
Self::build( 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,
None, None,
Capability::ReadWrite, Capability::ReadWrite,
@ -517,10 +517,10 @@ impl Buffer {
remote_id: BufferId, remote_id: BufferId,
replica_id: ReplicaId, replica_id: ReplicaId,
capability: Capability, capability: Capability,
base_text: String, base_text: impl Into<String>,
) -> Self { ) -> Self {
Self::build( Self::build(
TextBuffer::new(replica_id, remote_id, base_text), TextBuffer::new(replica_id, remote_id, base_text.into()),
None, None,
None, None,
capability, capability,

View File

@ -44,12 +44,8 @@ fn test_line_endings(cx: &mut gpui::AppContext) {
init_settings(cx, |_| {}); init_settings(cx, |_| {});
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new( let mut buffer =
0, Buffer::local("one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"one\r\ntwo\rthree",
)
.with_language(Arc::new(rust_lang()), cx);
assert_eq!(buffer.text(), "one\ntwo\nthree"); assert_eq!(buffer.text(), "one\ntwo\nthree");
assert_eq!(buffer.line_ending(), LineEnding::Windows); 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_1_events = Arc::new(Mutex::new(Vec::new()));
let buffer_2_events = Arc::new(Mutex::new(Vec::new())); let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
let buffer1 = cx let buffer1 = cx.new_model(|cx| Buffer::local("abcdef", cx));
.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef")); let buffer2 = cx.new_model(|cx| {
let buffer2 = cx Buffer::remote(
.new_model(|cx| Buffer::new(1, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef")); BufferId::from(cx.entity_id().as_non_zero_u64()),
1,
Capability::ReadWrite,
"abcdef",
)
});
let buffer1_ops = Arc::new(Mutex::new(Vec::new())); let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
buffer1.update(cx, { buffer1.update(cx, {
let buffer1_ops = buffer1_ops.clone(); let buffer1_ops = buffer1_ops.clone();
@ -338,8 +339,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) {
#[gpui::test] #[gpui::test]
async fn test_apply_diff(cx: &mut TestAppContext) { async fn test_apply_diff(cx: &mut TestAppContext) {
let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n"; let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
let buffer = let buffer = cx.new_model(|cx| Buffer::local(text, cx));
cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text));
let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3))); let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
let text = "a\nccc\ndddd\nffffff\n"; let text = "a\nccc\ndddd\nffffff\n";
@ -371,8 +371,7 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
] ]
.join("\n"); .join("\n");
let buffer = let buffer = cx.new_model(|cx| Buffer::local(text, cx));
cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text));
// Spawn a task to format the buffer's whitespace. // Spawn a task to format the buffer's whitespace.
// Pause so that the foratting task starts running. // Pause so that the foratting task starts running.
@ -436,10 +435,8 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
#[gpui::test] #[gpui::test]
async fn test_reparse(cx: &mut gpui::TestAppContext) { async fn test_reparse(cx: &mut gpui::TestAppContext) {
let text = "fn a() {}"; let text = "fn a() {}";
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
.with_language(Arc::new(rust_lang()), cx)
});
// Wait for the initial text to parse // Wait for the initial text to parse
cx.executor().run_until_parked(); cx.executor().run_until_parked();
@ -566,8 +563,7 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) {
#[gpui::test] #[gpui::test]
async fn test_resetting_language(cx: &mut gpui::TestAppContext) { async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
let buffer = cx.new_model(|cx| { 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).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
buffer.set_sync_parse_timeout(Duration::ZERO); buffer.set_sync_parse_timeout(Duration::ZERO);
buffer buffer
}); });
@ -615,10 +611,8 @@ async fn test_outline(cx: &mut gpui::TestAppContext) {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
.with_language(Arc::new(rust_lang()), cx)
});
let outline = buffer let outline = buffer
.update(cx, |buffer, _| buffer.snapshot().outline(None)) .update(cx, |buffer, _| buffer.snapshot().outline(None))
.unwrap(); .unwrap();
@ -702,10 +696,8 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
.with_language(Arc::new(rust_lang()), cx)
});
let outline = buffer let outline = buffer
.update(cx, |buffer, _| buffer.snapshot().outline(None)) .update(cx, |buffer, _| buffer.snapshot().outline(None))
.unwrap(); .unwrap();
@ -741,10 +733,7 @@ async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(Arc::new(language), cx)
});
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
// extra context nodes are included in the outline. // extra context nodes are included in the outline.
@ -786,10 +775,8 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer =
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
.with_language(Arc::new(rust_lang()), cx)
});
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
// point is at the start of an item // 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) { fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
cx.new_model(|cx| { cx.new_model(|cx| {
let text = "fn a() { b(|c| {}) }"; let text = "fn a() { b(|c| {}) }";
let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
let snapshot = buffer.snapshot(); let snapshot = buffer.snapshot();
assert_eq!( assert_eq!(
@ -1051,8 +1037,7 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
cx.new_model(|cx| { cx.new_model(|cx| {
let text = "fn a() {}"; let text = "fn a() {}";
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx); buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
assert_eq!(buffer.text(), "fn a() {\n \n}"); 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| { cx.new_model(|cx| {
let text = "fn a() {}"; let text = "fn a() {}";
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx); buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
assert_eq!(buffer.text(), "fn a() {\n\t\n}"); 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, |_| {}); init_settings(cx, |_| {});
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new( let mut buffer = Buffer::local(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
" "
fn a() { fn a() {
c; c;
@ -1144,6 +1126,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC
} }
" "
.unindent(), .unindent(),
cx,
) )
.with_language(Arc::new(rust_lang()), 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| { cx.new_model(|cx| {
eprintln!("second buffer: {:?}", cx.entity_id()); eprintln!("second buffer: {:?}", cx.entity_id());
let mut buffer = Buffer::new( let mut buffer = Buffer::local(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
" "
fn a() { fn a() {
b(); b();
@ -1219,6 +1200,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC
" "
.replace('|', "") // marker to preserve trailing whitespace .replace('|', "") // marker to preserve trailing whitespace
.unindent(), .unindent(),
cx,
) )
.with_language(Arc::new(rust_lang()), 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, |_| {}); init_settings(cx, |_| {});
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new( let mut buffer = Buffer::local(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
" "
fn a() { fn a() {
i i
} }
" "
.unindent(), .unindent(),
cx,
) )
.with_language(Arc::new(rust_lang()), 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, |_| {}); init_settings(cx, |_| {});
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new( let mut buffer = Buffer::local(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
" "
fn a() {} fn a() {}
" "
.unindent(), .unindent(),
cx,
) )
.with_language(Arc::new(rust_lang()), 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| { cx.new_model(|cx| {
let text = "a\nb"; let text = "a\nb";
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
buffer.edit( buffer.edit(
[(0..1, "\n"), (2..3, "\n")], [(0..1, "\n"), (2..3, "\n")],
Some(AutoindentMode::EachLine), Some(AutoindentMode::EachLine),
@ -1421,8 +1400,7 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
" "
.unindent(); .unindent();
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
buffer.edit( buffer.edit(
[(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")], [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
Some(AutoindentMode::EachLine), Some(AutoindentMode::EachLine),
@ -1459,8 +1437,7 @@ fn test_autoindent_block_mode(cx: &mut AppContext) {
} }
"# "#
.unindent(); .unindent();
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
// When this text was copied, both of the quotation marks were at the same // 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 // 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(); .unindent();
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
.with_language(Arc::new(rust_lang()), cx);
// The original indent columns are not known, so this text is // The original indent columns are not known, so this text is
// auto-indented in a block as if the first line was copied in // 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(); .unindent();
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) let mut buffer = Buffer::local(text, cx).with_language(
.with_language( Arc::new(Language::new(
Arc::new(Language::new( LanguageConfig {
LanguageConfig { name: "Markdown".into(),
name: "Markdown".into(), auto_indent_using_last_non_empty_line: false,
auto_indent_using_last_non_empty_line: false, ..Default::default()
..Default::default() },
}, Some(tree_sitter_json::language()),
Some(tree_sitter_json::language()), )),
)), cx,
cx, );
);
buffer.edit( buffer.edit(
[(Point::new(3, 0)..Point::new(3, 0), "\n")], [(Point::new(3, 0)..Point::new(3, 0), "\n")],
Some(AutoindentMode::EachLine), Some(AutoindentMode::EachLine),
@ -1702,7 +1677,7 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
false, 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_registry(language_registry);
buffer.set_language(Some(html_language), cx); buffer.set_language(Some(html_language), cx);
buffer.edit( buffer.edit(
@ -1738,8 +1713,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
}); });
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
.with_language(Arc::new(ruby_lang()), cx);
let text = r#" let text = r#"
class C class C
@ -1840,8 +1814,7 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
"# "#
.unindent(); .unindent();
let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), &text) let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
.with_language(Arc::new(language), cx);
let snapshot = buffer.snapshot(); let snapshot = buffer.snapshot();
let config = snapshot.language_scope_at(0).unwrap(); let config = snapshot.language_scope_at(0).unwrap();
@ -1958,12 +1931,7 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) {
"# "#
.unindent(); .unindent();
let buffer = Buffer::new( let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
text.clone(),
)
.with_language(Arc::new(language), cx);
let snapshot = buffer.snapshot(); let snapshot = buffer.snapshot();
// By default, all brackets are enabled // 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(html_lang()));
language_registry.add(Arc::new(erb_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_registry(language_registry.clone());
buffer.set_language( buffer.set_language(
language_registry language_registry
@ -2042,7 +2010,7 @@ fn test_serialization(cx: &mut gpui::AppContext) {
let mut now = Instant::now(); let mut now = Instant::now();
let buffer1 = cx.new_model(|cx| { 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); buffer.edit([(3..3, "D")], None, cx);
now += Duration::from_secs(1); 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 replica_ids = Vec::new();
let mut buffers = Vec::new(); let mut buffers = Vec::new();
let network = Arc::new(Mutex::new(Network::new(rng.clone()))); let network = Arc::new(Mutex::new(Network::new(rng.clone())));
let base_buffer = cx.new_model(|cx| { let base_buffer = cx.new_model(|cx| Buffer::local(base_text.as_str(), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
base_text.as_str(),
)
});
for i in 0..rng.gen_range(min_peers..=max_peers) { for i in 0..rng.gen_range(min_peers..=max_peers) {
let buffer = cx.new_model(|cx| { 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 (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| {
Buffer::new( Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx)
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
expected_text.clone(),
)
.with_language(Arc::new(language), cx)
}); });
let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot()); let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());

View File

@ -297,7 +297,6 @@ mod tests {
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
use settings::SettingsStore; use settings::SettingsStore;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use text::BufferId;
#[gpui::test] #[gpui::test]
async fn test_c_autoindent(cx: &mut TestAppContext) { async fn test_c_autoindent(cx: &mut TestAppContext) {
@ -315,8 +314,7 @@ mod tests {
let language = crate::language("c", tree_sitter_c::language()); let language = crate::language("c", tree_sitter_c::language());
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") let mut buffer = Buffer::local("", cx).with_language(language, cx);
.with_language(language, cx);
// empty function // empty function
buffer.edit([(0..0, "int main() {}")], None, cx); buffer.edit([(0..0, "int main() {}")], None, cx);

View File

@ -136,7 +136,6 @@ async fn get_cached_server_binary(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use gpui::{Context, TestAppContext}; use gpui::{Context, TestAppContext};
use text::BufferId;
use unindent::Unindent; use unindent::Unindent;
#[gpui::test] #[gpui::test]
@ -168,10 +167,8 @@ mod tests {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer =
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) cx.new_model(|cx| language::Buffer::local(text, cx).with_language(language, cx));
.with_language(language, cx)
});
let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
assert_eq!( assert_eq!(
outline outline

View File

@ -186,7 +186,6 @@ mod tests {
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
use settings::SettingsStore; use settings::SettingsStore;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use text::BufferId;
#[gpui::test] #[gpui::test]
async fn test_python_autoindent(cx: &mut TestAppContext) { async fn test_python_autoindent(cx: &mut TestAppContext) {
@ -204,8 +203,7 @@ mod tests {
}); });
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") let mut buffer = Buffer::local("", cx).with_language(language, cx);
.with_language(language, cx);
let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext<Buffer>| { let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext<Buffer>| {
let ix = buffer.len(); let ix = buffer.len();
buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx); buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);

View File

@ -465,7 +465,6 @@ mod tests {
use gpui::{BorrowAppContext, Context, Hsla, TestAppContext}; use gpui::{BorrowAppContext, Context, Hsla, TestAppContext};
use language::language_settings::AllLanguageSettings; use language::language_settings::AllLanguageSettings;
use settings::SettingsStore; use settings::SettingsStore;
use text::BufferId;
use theme::SyntaxTheme; use theme::SyntaxTheme;
#[gpui::test] #[gpui::test]
@ -682,8 +681,7 @@ mod tests {
let language = crate::language("rust", tree_sitter_rust::language()); let language = crate::language("rust", tree_sitter_rust::language());
cx.new_model(|cx| { cx.new_model(|cx| {
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") let mut buffer = Buffer::local("", cx).with_language(language, cx);
.with_language(language, cx);
// indent between braces // indent between braces
buffer.set_text("fn a() {}", cx); buffer.set_text("fn a() {}", cx);

View File

@ -413,7 +413,6 @@ async fn get_cached_eslint_server_binary(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use gpui::{Context, TestAppContext}; use gpui::{Context, TestAppContext};
use text::BufferId;
use unindent::Unindent; use unindent::Unindent;
#[gpui::test] #[gpui::test]
@ -435,10 +434,8 @@ mod tests {
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer =
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) cx.new_model(|cx| language::Buffer::local(text, cx).with_language(language, cx));
.with_language(language, cx)
});
let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
assert_eq!( assert_eq!(
outline outline

View File

@ -1700,8 +1700,7 @@ impl MultiBuffer {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
impl MultiBuffer { impl MultiBuffer {
pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model<Self> { pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model<Self> {
let buffer = cx let buffer = cx.new_model(|cx| Buffer::local(text, cx));
.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text));
cx.new_model(|cx| Self::singleton(buffer, cx)) cx.new_model(|cx| Self::singleton(buffer, cx))
} }
@ -1711,9 +1710,7 @@ impl MultiBuffer {
) -> Model<Self> { ) -> Model<Self> {
let multi = cx.new_model(|_| Self::new(0, Capability::ReadWrite)); let multi = cx.new_model(|_| Self::new(0, Capability::ReadWrite));
for (text, ranges) in excerpts { for (text, ranges) in excerpts {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(text, cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
});
let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange { let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
context: range, context: range,
primary: None, primary: None,
@ -1802,9 +1799,7 @@ impl MultiBuffer {
if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) { if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) {
let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() { let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() {
let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>(); let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
buffers.push(cx.new_model(|cx| { buffers.push(cx.new_model(|cx| Buffer::local(text, cx)));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
}));
let buffer = buffers.last().unwrap().read(cx); let buffer = buffers.last().unwrap().read(cx);
log::info!( log::info!(
"Creating new buffer {} with text: {:?}", "Creating new buffer {} with text: {:?}",
@ -4327,13 +4322,7 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_singleton(cx: &mut AppContext) { fn test_singleton(cx: &mut AppContext) {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(6, 6, 'a'),
)
});
let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
let snapshot = multibuffer.read(cx).snapshot(cx); let snapshot = multibuffer.read(cx).snapshot(cx);
@ -4360,8 +4349,7 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_remote(cx: &mut AppContext) { fn test_remote(cx: &mut AppContext) {
let host_buffer = let host_buffer = cx.new_model(|cx| Buffer::local("a", cx));
cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "a"));
let guest_buffer = cx.new_model(|cx| { let guest_buffer = cx.new_model(|cx| {
let state = host_buffer.read(cx).to_proto(); let state = host_buffer.read(cx).to_proto();
let ops = cx let ops = cx
@ -4392,20 +4380,8 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) { fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
Buffer::new( let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
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 multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let events = Arc::new(RwLock::new(Vec::<Event>::new())); let events = Arc::new(RwLock::new(Vec::<Event>::new()));
@ -4638,20 +4614,8 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_excerpt_events(cx: &mut AppContext) { fn test_excerpt_events(cx: &mut AppContext) {
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'a'), cx));
Buffer::new( let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'm'), cx));
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 leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let follower_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] #[gpui::test]
fn test_push_excerpts_with_context_lines(cx: &mut AppContext) { fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(20, 3, 'a'),
)
});
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts_with_context_lines( multibuffer.push_excerpts_with_context_lines(
@ -4798,13 +4756,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) { async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(20, 3, 'a'),
)
});
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
let snapshot = buffer.read(cx); let snapshot = buffer.read(cx);
@ -4850,9 +4802,7 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_singleton_multibuffer_anchors(cx: &mut AppContext) { fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local("abcd", cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd")
});
let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
let old_snapshot = multibuffer.read(cx).snapshot(cx); let old_snapshot = multibuffer.read(cx).snapshot(cx);
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
@ -4872,12 +4822,8 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_multibuffer_anchors(cx: &mut AppContext) { fn test_multibuffer_anchors(cx: &mut AppContext) {
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") let buffer_2 = cx.new_model(|cx| Buffer::local("efghi", cx));
});
let buffer_2 = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "efghi")
});
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite); let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
@ -4934,16 +4880,8 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) { fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") let buffer_2 = cx.new_model(|cx| Buffer::local("ABCDEFGHIJKLMNOP", cx));
});
let buffer_2 = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"ABCDEFGHIJKLMNOP",
)
});
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
// Create an insertion id in buffer 1 that doesn't exist in buffer 2. // 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) let base_text = util::RandomCharIter::new(&mut rng)
.take(10) .take(10)
.collect::<String>(); .collect::<String>();
buffers.push(cx.new_model(|cx| { buffers.push(cx.new_model(|cx| Buffer::local(base_text, cx)));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
base_text,
)
}));
buffers.last().unwrap() buffers.last().unwrap()
} else { } else {
buffers.choose(&mut rng).unwrap() buffers.choose(&mut rng).unwrap()
@ -5502,12 +5434,8 @@ mod tests {
let test_settings = SettingsStore::test(cx); let test_settings = SettingsStore::test(cx);
cx.set_global(test_settings); cx.set_global(test_settings);
let buffer_1 = cx.new_model(|cx| { let buffer_1 = cx.new_model(|cx| Buffer::local("1234", cx));
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "1234") let buffer_2 = cx.new_model(|cx| Buffer::local("5678", cx));
});
let buffer_2 = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "5678")
});
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let group_interval = multibuffer.read(cx).history.group_interval; let group_interval = multibuffer.read(cx).history.group_interval;
multibuffer.update(cx, |multibuffer, cx| { multibuffer.update(cx, |multibuffer, cx| {

View File

@ -178,7 +178,6 @@ pub struct Project {
collaborators: HashMap<proto::PeerId, Collaborator>, collaborators: HashMap<proto::PeerId, Collaborator>,
client_subscriptions: Vec<client::Subscription>, client_subscriptions: Vec<client::Subscription>,
_subscriptions: Vec<gpui::Subscription>, _subscriptions: Vec<gpui::Subscription>,
next_buffer_id: BufferId,
loading_buffers: HashMap<BufferId, Vec<oneshot::Sender<Result<Model<Buffer>, anyhow::Error>>>>, loading_buffers: HashMap<BufferId, Vec<oneshot::Sender<Result<Model<Buffer>, anyhow::Error>>>>,
incomplete_remote_buffers: HashMap<BufferId, Model<Buffer>>, incomplete_remote_buffers: HashMap<BufferId, Model<Buffer>>,
shared_buffers: HashMap<proto::PeerId, HashSet<BufferId>>, shared_buffers: HashMap<proto::PeerId, HashSet<BufferId>>,
@ -675,7 +674,6 @@ impl Project {
flush_language_server_update: None, flush_language_server_update: None,
pending_language_server_update: None, pending_language_server_update: None,
collaborators: Default::default(), collaborators: Default::default(),
next_buffer_id: BufferId::new(1).unwrap(),
opened_buffers: Default::default(), opened_buffers: Default::default(),
shared_buffers: Default::default(), shared_buffers: Default::default(),
loading_buffers_by_path: Default::default(), loading_buffers_by_path: Default::default(),
@ -792,7 +790,6 @@ impl Project {
pending_language_server_update: None, pending_language_server_update: None,
flush_language_server_update: None, flush_language_server_update: None,
loading_buffers_by_path: Default::default(), loading_buffers_by_path: Default::default(),
next_buffer_id: BufferId::new(1).unwrap(),
loading_buffers: Default::default(), loading_buffers: Default::default(),
shared_buffers: Default::default(), shared_buffers: Default::default(),
incomplete_remote_buffers: Default::default(), incomplete_remote_buffers: Default::default(),
@ -1849,9 +1846,8 @@ impl Project {
if self.is_remote() { if self.is_remote() {
return Err(anyhow!("creating buffers as a guest is not supported yet")); 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| { 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) .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx)
}); });
self.register_buffer(&buffer, cx)?; self.register_buffer(&buffer, cx)?;
@ -1950,10 +1946,9 @@ impl Project {
worktree: Model<Worktree>, worktree: Model<Worktree>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<Model<Buffer>>> { ) -> Task<Result<Model<Buffer>>> {
let buffer_id = self.next_buffer_id.next();
let load_buffer = worktree.update(cx, |worktree, cx| { let load_buffer = worktree.update(cx, |worktree, cx| {
let worktree = worktree.as_local_mut().unwrap(); 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 { fn is_not_found_error(error: &anyhow::Error) -> bool {
error error
@ -1967,7 +1962,7 @@ impl Project {
Err(error) if is_not_found_error(&error) => { Err(error) if is_not_found_error(&error) => {
worktree.update(&mut cx, |worktree, cx| { worktree.update(&mut cx, |worktree, cx| {
let worktree = worktree.as_local_mut().unwrap(); let worktree = worktree.as_local_mut().unwrap();
worktree.new_buffer(buffer_id, path, cx) worktree.new_buffer(path, cx)
}) })
} }
Err(e) => Err(e), Err(e) => Err(e),

View File

@ -1141,7 +1141,7 @@ mod tests {
use super::*; use super::*;
use editor::{DisplayPoint, Editor}; use editor::{DisplayPoint, Editor};
use gpui::{Context, Hsla, TestAppContext, VisualTestContext}; use gpui::{Context, Hsla, TestAppContext, VisualTestContext};
use language::{Buffer, BufferId}; use language::Buffer;
use smol::stream::StreamExt as _; use smol::stream::StreamExt as _;
use unindent::Unindent as _; use unindent::Unindent as _;
@ -1161,9 +1161,7 @@ mod tests {
) -> (View<Editor>, View<BufferSearchBar>, &mut VisualTestContext) { ) -> (View<Editor>, View<BufferSearchBar>, &mut VisualTestContext) {
init_globals(cx); init_globals(cx);
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| {
Buffer::new( Buffer::local(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
r#" r#"
A regular expression (shortened as regex or regexp;[1] also referred to as 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 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. for "find" or "find and replace" operations on strings, or for input validation.
"# "#
.unindent(), .unindent(),
cx,
) )
}); });
let cx = cx.add_empty_window(); let cx = cx.add_empty_window();
@ -1519,13 +1518,7 @@ mod tests {
expected_query_matches_count > 1, expected_query_matches_count > 1,
"Should pick a query with multiple results" "Should pick a query with multiple results"
); );
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(buffer_text, cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
buffer_text,
)
});
let window = cx.add_window(|_| gpui::Empty); let window = cx.add_window(|_| gpui::Empty);
let editor = window.build_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); 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. for "find" or "find and replace" operations on strings, or for input validation.
"# "#
.unindent(); .unindent();
let buffer = cx.new_model(|cx| { let buffer = cx.new_model(|cx| Buffer::local(buffer_text, cx));
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
buffer_text,
)
});
let cx = cx.add_empty_window(); let cx = cx.add_empty_window();
let editor = cx.new_view(|cx| Editor::for_buffer(buffer.clone(), None, cx)); let editor = cx.new_view(|cx| Editor::for_buffer(buffer.clone(), None, cx));

View File

@ -71,12 +71,19 @@ impl Display for BufferId {
} }
} }
impl From<NonZeroU64> for BufferId {
fn from(id: NonZeroU64) -> Self {
BufferId(id)
}
}
impl BufferId { impl BufferId {
/// Returns Err if `id` is outside of BufferId domain. /// Returns Err if `id` is outside of BufferId domain.
pub fn new(id: u64) -> anyhow::Result<Self> { pub fn new(id: u64) -> anyhow::Result<Self> {
let id = NonZeroU64::new(id).context("Buffer id cannot be 0.")?; let id = NonZeroU64::new(id).context("Buffer id cannot be 0.")?;
Ok(Self(id)) Ok(Self(id))
} }
/// Increments this buffer id, returning the old value. /// Increments this buffer id, returning the old value.
/// So that's a post-increment operator in disguise. /// So that's a post-increment operator in disguise.
pub fn next(&mut self) -> Self { pub fn next(&mut self) -> Self {

View File

@ -73,14 +73,14 @@ mod test {
use crate::{test::VimTestContext, Vim}; use crate::{test::VimTestContext, Vim};
use editor::Editor; use editor::Editor;
use gpui::{Context, Entity, VisualTestContext}; use gpui::{Context, Entity, VisualTestContext};
use language::{Buffer, BufferId}; use language::Buffer;
// regression test for blur called with a different active editor // regression test for blur called with a different active editor
#[gpui::test] #[gpui::test]
async fn test_blur_focus(cx: &mut gpui::TestAppContext) { async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await; 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 window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
let editor2 = cx let editor2 = cx
.update(|cx| { .update(|cx| {
@ -115,7 +115,7 @@ mod test {
let mut cx1 = VisualTestContext::from_window(cx.window, &cx); let mut cx1 = VisualTestContext::from_window(cx.window, &cx);
let editor1 = cx.editor.clone(); 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)); let (editor2, cx2) = cx.add_window_view(|cx| Editor::for_buffer(buffer, None, cx));
editor2.update(cx2, |_, cx| { editor2.update(cx2, |_, cx| {

View File

@ -751,20 +751,21 @@ impl LocalWorktree {
pub fn load_buffer( pub fn load_buffer(
&mut self, &mut self,
id: BufferId,
path: &Path, path: &Path,
cx: &mut ModelContext<Worktree>, cx: &mut ModelContext<Worktree>,
) -> Task<Result<Model<Buffer>>> { ) -> Task<Result<Model<Buffer>>> {
let path = Arc::from(path); 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 { cx.spawn(move |this, mut cx| async move {
let (file, contents, diff_base) = this let (file, contents, diff_base) = this
.update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))? .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))?
.await?; .await?;
let text_buffer = cx let text_buffer = cx
.background_executor() .background_executor()
.spawn(async move { text::Buffer::new(0, id, contents) }) .spawn(async move { text::Buffer::new(0, buffer_id, contents) })
.await; .await;
cx.new_model(|_| { cx.insert_model(reservation, |_| {
Buffer::build( Buffer::build(
text_buffer, text_buffer,
diff_base, diff_base,
@ -777,13 +778,13 @@ impl LocalWorktree {
pub fn new_buffer( pub fn new_buffer(
&mut self, &mut self,
buffer_id: BufferId,
path: Arc<Path>, path: Arc<Path>,
cx: &mut ModelContext<Worktree>, cx: &mut ModelContext<Worktree>,
) -> Model<Buffer> { ) -> Model<Buffer> {
let text_buffer = text::Buffer::new(0, buffer_id, "".into());
let worktree = cx.handle(); 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( Buffer::build(
text_buffer, text_buffer,
None, None,

View File

@ -21,7 +21,6 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
}; };
use text::BufferId;
use util::{http::FakeHttpClient, test::temp_tree, ResultExt}; use util::{http::FakeHttpClient, test::temp_tree, ResultExt};
#[gpui::test] #[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 prev_read_dir_count = fs.read_dir_call_count();
let buffer = tree let buffer = tree
.update(cx, |tree, cx| { .update(cx, |tree, cx| {
tree.as_local_mut().unwrap().load_buffer( tree.as_local_mut()
BufferId::new(1).unwrap(), .unwrap()
"one/node_modules/b/b1.js".as_ref(), .load_buffer("one/node_modules/b/b1.js".as_ref(), cx)
cx,
)
}) })
.await .await
.unwrap(); .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 prev_read_dir_count = fs.read_dir_call_count();
let buffer = tree let buffer = tree
.update(cx, |tree, cx| { .update(cx, |tree, cx| {
tree.as_local_mut().unwrap().load_buffer( tree.as_local_mut()
BufferId::new(1).unwrap(), .unwrap()
"one/node_modules/a/a2.js".as_ref(), .load_buffer("one/node_modules/a/a2.js".as_ref(), cx)
cx,
)
}) })
.await .await
.unwrap(); .unwrap();