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,
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,

View File

@ -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);

View File

@ -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();

View File

@ -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(

View File

@ -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));

View File

@ -1328,37 +1328,19 @@ impl InlayHintRefreshReason {
impl Editor {
pub fn single_line(cx: &mut ViewContext<Self>) -> 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>) -> 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>) -> 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)
}

View File

@ -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::<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();
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::<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();
}
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);

View File

@ -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(

View File

@ -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 {

View File

@ -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<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
/// entity along with a `ModelContext` for the entity.
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>
where
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>(
&self,
window: &WindowHandle<T>,

View File

@ -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<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>(
&mut self,
handle: &Model<T>,
@ -278,6 +300,19 @@ impl Context for AsyncWindowContext {
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>(
&mut self,
handle: &Model<T>,
@ -287,13 +322,6 @@ impl Context for AsyncWindowContext {
.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>(
&self,
handle: &Model<T>,
@ -305,6 +333,13 @@ impl Context for AsyncWindowContext {
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>(
&self,
window: &WindowHandle<T>,

View File

@ -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<u64> 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()
}

View File

@ -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<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>(
&mut self,
handle: &Model<U>,
@ -237,13 +250,6 @@ impl<'a, T> Context for ModelContext<'a, T> {
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>(
&self,
handle: &Model<U>,
@ -255,6 +261,13 @@ impl<'a, T> Context for ModelContext<'a, T> {
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>(
&self,
window: &WindowHandle<U>,

View File

@ -42,6 +42,20 @@ impl Context for TestAppContext {
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>(
&mut self,
handle: &Model<T>,
@ -803,6 +817,18 @@ impl Context for VisualTestContext {
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>(
&mut self,
handle: &Model<T>,

View File

@ -165,6 +165,19 @@ pub trait Context {
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> 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.
fn update_model<T, R>(
&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<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
/// require a window to be present.
pub trait VisualContext: Context {

View File

@ -1830,6 +1830,18 @@ impl Context for WindowContext<'_> {
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>(
&mut self,
model: &Model<T>,
@ -1844,18 +1856,6 @@ impl Context for WindowContext<'_> {
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>(
&self,
handle: &Model<T>,
@ -1868,6 +1868,18 @@ impl Context for WindowContext<'_> {
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>(
&self,
window: &WindowHandle<T>,
@ -2478,6 +2490,18 @@ impl<V> Context for ViewContext<'_, V> {
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>(
&mut self,
model: &Model<T>,
@ -2486,13 +2510,6 @@ impl<V> Context for ViewContext<'_, V> {
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>(
&self,
handle: &Model<T>,
@ -2504,6 +2521,13 @@ impl<V> Context for ViewContext<'_, V> {
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>(
&self,
window: &WindowHandle<T>,

View File

@ -264,6 +264,18 @@ impl<'a> Context for ElementContext<'a> {
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>(
&mut self,
handle: &crate::Model<T>,

View File

@ -503,9 +503,9 @@ pub enum CharKind {
impl Buffer {
/// 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(
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<String>,
) -> Self {
Self::build(
TextBuffer::new(replica_id, remote_id, base_text),
TextBuffer::new(replica_id, remote_id, base_text.into()),
None,
None,
capability,

View File

@ -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());

View File

@ -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);

View File

@ -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

View File

@ -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<Buffer>| {
let ix = buffer.len();
buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);

View File

@ -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);

View File

@ -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

View File

@ -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<Self> {
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<Self> {
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::<String>();
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::<Event>::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::<String>();
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| {

View File

@ -178,7 +178,6 @@ pub struct Project {
collaborators: HashMap<proto::PeerId, Collaborator>,
client_subscriptions: Vec<client::Subscription>,
_subscriptions: Vec<gpui::Subscription>,
next_buffer_id: BufferId,
loading_buffers: HashMap<BufferId, Vec<oneshot::Sender<Result<Model<Buffer>, anyhow::Error>>>>,
incomplete_remote_buffers: HashMap<BufferId, Model<Buffer>>,
shared_buffers: HashMap<proto::PeerId, HashSet<BufferId>>,
@ -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<Worktree>,
cx: &mut ModelContext<Self>,
) -> Task<Result<Model<Buffer>>> {
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),

View File

@ -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<Editor>, View<BufferSearchBar>, &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));

View File

@ -71,12 +71,19 @@ impl Display for BufferId {
}
}
impl From<NonZeroU64> 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<Self> {
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 {

View File

@ -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| {

View File

@ -751,20 +751,21 @@ impl LocalWorktree {
pub fn load_buffer(
&mut self,
id: BufferId,
path: &Path,
cx: &mut ModelContext<Worktree>,
) -> Task<Result<Model<Buffer>>> {
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<Path>,
cx: &mut ModelContext<Worktree>,
) -> Model<Buffer> {
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,

View File

@ -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();