Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo
2021-05-03 16:33:13 -06:00
co-authored by Max Brunsfeld
parent afb623b6b5
commit b801628230
6 changed files with 19 additions and 225 deletions
-1
View File
@@ -1 +0,0 @@
Cargo.toml
+11 -11
View File
@@ -2408,7 +2408,7 @@ mod tests {
let mut reference_string = RandomCharIter::new(&mut rng)
.take(reference_string_len)
.collect::<String>();
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let mut buffer = Buffer::new(0, reference_string.as_str());
let mut buffer_versions = Vec::new();
for _i in 0..10 {
@@ -2494,7 +2494,7 @@ mod tests {
#[test]
fn test_line_len() {
App::test((), |ctx| {
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let mut buffer = Buffer::new(0, "");
buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap();
buffer.edit(vec![12..12], "kl\nmno", None).unwrap();
@@ -2516,7 +2516,7 @@ mod tests {
#[test]
fn test_rightmost_point() {
App::test((), |ctx| {
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let mut buffer = Buffer::new(0, "");
assert_eq!(buffer.rightmost_point().row, 0);
buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap();
@@ -2537,7 +2537,7 @@ mod tests {
#[test]
fn test_text_summary_for_range() {
App::test((), |ctx| {
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz");
let text = Text::from(buffer.text());
assert_eq!(
@@ -2568,7 +2568,7 @@ mod tests {
#[test]
fn test_chars_at() {
App::test((), |ctx| {
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let mut buffer = Buffer::new(0, "");
buffer.edit(vec![0..0], "abcd\nefgh\nij", None).unwrap();
buffer.edit(vec![12..12], "kl\nmno", None).unwrap();
@@ -2720,7 +2720,7 @@ mod tests {
#[test]
fn test_anchors() {
App::test((), |ctx| {
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let mut buffer = Buffer::new(0, "");
buffer.edit(vec![0..0], "abc", None).unwrap();
let left_anchor = buffer.anchor_before(2).unwrap();
@@ -2885,7 +2885,7 @@ mod tests {
#[test]
fn test_anchors_at_start_and_end() {
App::test((), |ctx| {
ctx.add_model(|ctx| {
ctx.add_model(|_| {
let mut buffer = Buffer::new(0, "");
let before_start_anchor = buffer.anchor_before(0).unwrap();
let after_end_anchor = buffer.anchor_after(0).unwrap();
@@ -2913,7 +2913,7 @@ mod tests {
#[test]
fn test_is_modified() {
App::test((), |app| {
let model = app.add_model(|ctx| Buffer::new(0, "abc"));
let model = app.add_model(|_| Buffer::new(0, "abc"));
let events = Rc::new(RefCell::new(Vec::new()));
// initially, the buffer isn't dirty.
@@ -3000,7 +3000,7 @@ mod tests {
#[test]
fn test_undo_redo() {
App::test((), |app| {
app.add_model(|ctx| {
app.add_model(|_| {
let mut buffer = Buffer::new(0, "1234");
let edit1 = buffer.edit(vec![1..1], "abx", None).unwrap();
@@ -3037,7 +3037,7 @@ mod tests {
#[test]
fn test_history() {
App::test((), |app| {
app.add_model(|ctx| {
app.add_model(|_| {
let mut now = Instant::now();
let mut buffer = Buffer::new(0, "123456");
@@ -3124,7 +3124,7 @@ mod tests {
let mut network = Network::new();
for i in 0..PEERS {
let buffer =
ctx.add_model(|ctx| Buffer::new(i as ReplicaId, base_text.as_str()));
ctx.add_model(|_| Buffer::new(i as ReplicaId, base_text.as_str()));
buffers.push(buffer);
replica_ids.push(i as u16);
network.add_peer(i as u16);
+1 -14
View File
@@ -1344,19 +1344,6 @@ impl View for BufferView {
}
}
impl workspace::Item for Buffer {
type View = BufferView;
fn build_view(
buffer: ModelHandle<Self>,
settings: watch::Receiver<Settings>,
file: Option<FileHandle>,
ctx: &mut ViewContext<Self::View>,
) -> Self::View {
BufferView::for_buffer(buffer, file, settings, ctx)
}
}
impl workspace::ItemView for BufferView {
fn should_activate_item_on_event(event: &Self::Event) -> bool {
matches!(event, Event::Activate)
@@ -1421,7 +1408,7 @@ mod tests {
#[test]
fn test_selection_with_mouse() {
App::test((), |app| {
let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n"));
let buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n"));
let settings = settings::channel(&app.font_cache()).unwrap().1;
let (_, buffer_view) =
app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx));
+1 -1
View File
@@ -471,7 +471,7 @@ mod tests {
#[test]
fn test_basic_folds() {
App::test((), |app| {
let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6)));
let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
let mut map = FoldMap::new(buffer.clone(), app.as_ref());
map.fold(
+5 -197
View File
@@ -1,89 +1,25 @@
use super::{ItemView, ItemViewHandle};
use super::ItemViewHandle;
use crate::{
editor::{Buffer, BufferView, History},
editor::{Buffer, BufferView},
settings::Settings,
time::ReplicaId,
watch,
worktree::{FileHandle, Worktree, WorktreeHandle as _},
worktree::{Worktree, WorktreeHandle as _},
};
use anyhow::anyhow;
use futures_core::future::LocalBoxFuture;
use gpui::{AppContext, Entity, Handle, ModelContext, ModelHandle, MutableAppContext, ViewContext};
use gpui::{AppContext, Entity, ModelContext, ModelHandle};
use smol::prelude::*;
use std::{collections::hash_map::Entry, future};
use std::{
collections::{HashMap, HashSet},
fmt::Debug,
path::{Path, PathBuf},
pin::Pin,
sync::Arc,
};
pub trait Item
where
Self: Sized,
{
type View: ItemView;
fn build_view(
handle: ModelHandle<Self>,
settings: watch::Receiver<Settings>,
file: Option<FileHandle>,
ctx: &mut ViewContext<Self::View>,
) -> Self::View;
}
pub trait ItemHandle: Debug + Send + Sync {
fn add_view(
&self,
window_id: usize,
settings: watch::Receiver<Settings>,
file: Option<FileHandle>,
app: &mut MutableAppContext,
) -> Box<dyn ItemViewHandle>;
fn id(&self) -> usize;
fn boxed_clone(&self) -> Box<dyn ItemHandle>;
}
impl<T: 'static + Item> ItemHandle for ModelHandle<T> {
fn add_view(
&self,
window_id: usize,
settings: watch::Receiver<Settings>,
file: Option<FileHandle>,
app: &mut MutableAppContext,
) -> Box<dyn ItemViewHandle> {
Box::new(app.add_view(window_id, |ctx| {
T::build_view(self.clone(), settings, file, ctx)
}))
}
fn id(&self) -> usize {
Handle::id(self)
}
fn boxed_clone(&self) -> Box<dyn ItemHandle> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn ItemHandle> {
fn clone(&self) -> Self {
self.boxed_clone()
}
}
pub type OpenResult = Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>;
#[derive(Clone)]
enum OpenedItem {
Loading(watch::Receiver<Option<OpenResult>>),
Loaded(Box<dyn ItemHandle>),
}
pub struct Workspace {
replica_id: ReplicaId,
worktrees: HashSet<ModelHandle<Worktree>>,
items: HashMap<(usize, u64), OpenedItem>,
buffers: HashMap<
(usize, u64),
postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
@@ -95,7 +31,6 @@ impl Workspace {
let mut workspace = Self {
replica_id: 0,
worktrees: Default::default(),
items: Default::default(),
buffers: Default::default(),
};
workspace.open_paths(&paths, ctx);
@@ -156,7 +91,7 @@ impl Workspace {
(worktree_id, Path::new("").into())
}
pub fn open_entry2(
pub fn open_entry(
&mut self,
(worktree_id, path): (usize, Arc<Path>),
window_id: usize,
@@ -228,83 +163,6 @@ impl Workspace {
.boxed_local()
}
pub fn open_entry(
&mut self,
(worktree_id, path): (usize, Arc<Path>),
ctx: &mut ModelContext<'_, Self>,
) -> anyhow::Result<Pin<Box<dyn Future<Output = (OpenResult, FileHandle)> + Send>>> {
let worktree = self
.worktrees
.get(&worktree_id)
.cloned()
.ok_or_else(|| anyhow!("worktree {} does not exist", worktree_id,))?;
let inode = worktree
.read(ctx)
.inode_for_path(&path)
.ok_or_else(|| anyhow!("path {:?} does not exist", path))?;
let file = worktree
.file(path.clone(), ctx.as_ref())
.ok_or_else(|| anyhow!("path {:?} does not exist", path))?;
let item_key = (worktree_id, inode);
if let Some(item) = self.items.get(&item_key).cloned() {
return Ok(async move {
match item {
OpenedItem::Loaded(handle) => {
return (Ok(handle), file);
}
OpenedItem::Loading(rx) => loop {
rx.updated().await;
if let Some(result) = smol::block_on(rx.read()).clone() {
return (result, file);
}
},
}
}
.boxed());
}
let replica_id = self.replica_id;
let history = file.load_history(ctx.as_ref());
let (mut tx, rx) = watch::channel(None);
self.items.insert(item_key, OpenedItem::Loading(rx));
ctx.spawn(
history,
move |me, history: anyhow::Result<History>, ctx| match history {
Ok(history) => {
let handle =
Box::new(ctx.add_model(|_| Buffer::from_history(replica_id, history)))
as Box<dyn ItemHandle>;
me.items
.insert(item_key, OpenedItem::Loaded(handle.clone()));
ctx.spawn(
async move {
tx.update(|value| *value = Some(Ok(handle))).await;
},
|_, _, _| {},
)
.detach();
}
Err(error) => {
ctx.spawn(
async move {
tx.update(|value| *value = Some(Err(Arc::new(error)))).await;
},
|_, _, _| {},
)
.detach();
}
},
)
.detach();
self.open_entry((worktree_id, path), ctx)
}
fn on_worktree_updated(&mut self, _: ModelHandle<Worktree>, ctx: &mut ModelContext<Self>) {
ctx.notify();
}
@@ -334,53 +192,3 @@ impl WorkspaceHandle for ModelHandle<Workspace> {
.collect::<Vec<_>>()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::temp_tree;
use gpui::App;
use serde_json::json;
#[test]
fn test_open_entry() {
App::test_async((), |mut app| async move {
let dir = temp_tree(json!({
"a": {
"aa": "aa contents",
"ab": "ab contents",
},
}));
let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx));
app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx))
.await;
// Get the first file entry.
let tree = app.read(|ctx| workspace.read(ctx).worktrees.iter().next().unwrap().clone());
let path = app.read(|ctx| tree.read(ctx).files(0).next().unwrap().path().clone());
let entry = (tree.id(), path);
// Open the same entry twice before it finishes loading.
let (future_1, future_2) = workspace.update(&mut app, |w, app| {
(
w.open_entry(entry.clone(), app).unwrap(),
w.open_entry(entry.clone(), app).unwrap(),
)
});
let handle_1 = future_1.await.0.unwrap();
let handle_2 = future_2.await.0.unwrap();
assert_eq!(handle_1.id(), handle_2.id());
// Open the same entry again now that it has loaded
let handle_3 = workspace
.update(&mut app, |w, app| w.open_entry(entry, app).unwrap())
.await
.0
.unwrap();
assert_eq!(handle_3.id(), handle_1.id());
})
}
}
+1 -1
View File
@@ -245,7 +245,7 @@ impl WorkspaceView {
let window_id = ctx.window_id();
let future = self.workspace.update(ctx, |workspace, ctx| {
workspace.open_entry2(entry.clone(), window_id, self.settings.clone(), ctx)
workspace.open_entry(entry.clone(), window_id, self.settings.clone(), ctx)
});
Some(ctx.spawn(future, move |me, item_view, ctx| {