From 5fd084ec09dedb5ea0cfc08c1fba5cae40f4d2cc Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 4 May 2021 19:04:11 -0700 Subject: [PATCH 01/27] Start work on creating and saving new files --- Cargo.lock | 12 ++-- Cargo.toml | 10 +-- gpui/src/app.rs | 32 ++++++++- gpui/src/platform/mac/platform.rs | 41 ++++++++++- gpui/src/platform/mod.rs | 13 +++- gpui/src/platform/test.rs | 4 +- zed/src/editor/buffer_view.rs | 16 +++-- zed/src/menus.rs | 21 ++++-- zed/src/workspace.rs | 112 +++++++++++++++++++++--------- zed/src/worktree.rs | 41 ++++++----- 10 files changed, 227 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7fa8a95f7f79083e0f7cbc126f6561305aea5f7..e17c480da60b752d7f353c7915719691df77c093 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,7 @@ dependencies = [ [[package]] name = "cocoa" version = "0.24.0" -source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" dependencies = [ "bitflags 1.2.1", "block", @@ -464,7 +464,7 @@ dependencies = [ [[package]] name = "cocoa-foundation" version = "0.1.0" -source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" dependencies = [ "bitflags 1.2.1", "block", @@ -499,7 +499,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" version = "0.9.1" -source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" dependencies = [ "core-foundation-sys", "libc", @@ -508,12 +508,12 @@ dependencies = [ [[package]] name = "core-foundation-sys" version = "0.8.2" -source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" [[package]] name = "core-graphics" version = "0.22.2" -source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" dependencies = [ "bitflags 1.2.1", "core-foundation", @@ -525,7 +525,7 @@ dependencies = [ [[package]] name = "core-graphics-types" version = "0.1.1" -source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" dependencies = [ "bitflags 1.2.1", "core-foundation", diff --git a/Cargo.toml b/Cargo.toml index c58e56b67a4349d3dc7ada8b8019805ed603d37f..e1729a3a46d0180f3ab4baaa7a496bf980a02f78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,11 @@ members = ["zed", "gpui", "fsevent", "scoped_pool"] [patch.crates-io] async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"} -# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/454 -cocoa = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} -cocoa-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} -core-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} -core-graphics = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} +# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457 +cocoa = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} +cocoa-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} +core-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} +core-graphics = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} [profile.dev] split-debuginfo = "unpacked" diff --git a/gpui/src/app.rs b/gpui/src/app.rs index ac4c4e69b1782a416f5058025db0eafb974b7e7a..75d0ae01462be12ede526938f4126111c20ab076 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -21,7 +21,7 @@ use std::{ fmt::{self, Debug}, hash::{Hash, Hasher}, marker::PhantomData, - path::PathBuf, + path::{Path, PathBuf}, rc::{self, Rc}, sync::{Arc, Weak}, time::Duration, @@ -586,6 +586,22 @@ impl MutableAppContext { ); } + pub fn prompt_for_new_path(&self, directory: &Path, done_fn: F) + where + F: 'static + FnOnce(Option, &mut MutableAppContext), + { + let app = self.weak_self.as_ref().unwrap().upgrade().unwrap(); + let foreground = self.foreground.clone(); + self.platform().prompt_for_new_path( + directory, + Box::new(move |path| { + foreground + .spawn(async move { (done_fn)(path, &mut *app.borrow_mut()) }) + .detach(); + }), + ); + } + pub(crate) fn notify_view(&mut self, window_id: usize, view_id: usize) { self.pending_effects .push_back(Effect::ViewNotification { window_id, view_id }); @@ -1765,6 +1781,20 @@ impl<'a, T: View> ViewContext<'a, T> { &self.app.ctx.background } + pub fn prompt_for_paths(&self, options: PathPromptOptions, done_fn: F) + where + F: 'static + FnOnce(Option>, &mut MutableAppContext), + { + self.app.prompt_for_paths(options, done_fn) + } + + pub fn prompt_for_new_path(&self, directory: &Path, done_fn: F) + where + F: 'static + FnOnce(Option, &mut MutableAppContext), + { + self.app.prompt_for_new_path(directory, done_fn) + } + pub fn debug_elements(&self) -> crate::json::Value { self.app.debug_elements(self.window_id).unwrap() } diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index e9bf34d684373fb695c3340ae731ca43e73bbf26..29f75ed3ac81d5fc5a4d8342d5e7b08d0b54c435 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -5,7 +5,7 @@ use cocoa::{ appkit::{ NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard, - NSPasteboardTypeString, NSWindow, + NSPasteboardTypeString, NSSavePanel, NSWindow, }, base::{id, nil, selector}, foundation::{NSArray, NSAutoreleasePool, NSData, NSInteger, NSString, NSURL}, @@ -25,7 +25,7 @@ use std::{ convert::TryInto, ffi::{c_void, CStr}, os::raw::c_char, - path::PathBuf, + path::{Path, PathBuf}, ptr, rc::Rc, slice, str, @@ -305,6 +305,43 @@ impl platform::Platform for MacPlatform { } } + fn prompt_for_new_path( + &self, + directory: &Path, + done_fn: Box)>, + ) { + unsafe { + let panel = NSSavePanel::savePanel(nil); + let path = ns_string(directory.to_string_lossy().as_ref()); + let url = NSURL::fileURLWithPath_isDirectory_(nil, path, true.to_objc()); + panel.setDirectoryURL(url); + + let done_fn = Cell::new(Some(done_fn)); + let block = ConcreteBlock::new(move |response: NSModalResponse| { + let result = if response == NSModalResponse::NSModalResponseOk { + let url = panel.URL(); + let string = url.absoluteString(); + let string = std::ffi::CStr::from_ptr(string.UTF8String()) + .to_string_lossy() + .to_string(); + if let Some(path) = string.strip_prefix("file://") { + Some(PathBuf::from(path)) + } else { + None + } + } else { + None + }; + + if let Some(done_fn) = done_fn.take() { + (done_fn)(result); + } + }); + let block = block.copy(); + let _: () = msg_send![panel, beginWithCompletionHandler: block]; + } + } + fn fonts(&self) -> Arc { self.fonts.clone() } diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index b98d3a687bcd35a2dc22a4ca6cff600d5fea87ea..e5e81c424e729f3214f8af0b0ca07126d16e0e20 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -19,7 +19,13 @@ use crate::{ }; use async_task::Runnable; pub use event::Event; -use std::{any::Any, ops::Range, path::PathBuf, rc::Rc, sync::Arc}; +use std::{ + any::Any, + ops::Range, + path::{Path, PathBuf}, + rc::Rc, + sync::Arc, +}; pub trait Platform { fn on_menu_command(&self, callback: Box)>); @@ -45,6 +51,11 @@ pub trait Platform { options: PathPromptOptions, done_fn: Box>)>, ); + fn prompt_for_new_path( + &self, + directory: &Path, + done_fn: Box)>, + ); fn quit(&self); fn write_to_clipboard(&self, item: ClipboardItem); fn read_from_clipboard(&self) -> Option; diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 878449a0216e9a0a95447d9abaef16e089f8672c..de385bbf844a0493579500a18aee8726bd34c608 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -1,6 +1,6 @@ use crate::ClipboardItem; use pathfinder_geometry::vector::Vector2F; -use std::{any::Any, cell::RefCell, rc::Rc, sync::Arc}; +use std::{any::Any, cell::RefCell, path::Path, rc::Rc, sync::Arc}; struct Platform { dispatcher: Arc, @@ -77,6 +77,8 @@ impl super::Platform for Platform { ) { } + fn prompt_for_new_path(&self, _: &Path, _: Box)>) {} + fn write_to_clipboard(&self, item: ClipboardItem) { *self.current_clipboard_item.borrow_mut() = Some(item); } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 40055103c62e3c9529eb24f8b14250200c7d0633..58e4428d6004e3d1b31d4151be646b8510fea077 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -6,11 +6,10 @@ use crate::{settings::Settings, watch, workspace, worktree::FileHandle}; use anyhow::Result; use futures_core::future::LocalBoxFuture; use gpui::{ - fonts::Properties as FontProperties, keymap::Binding, text_layout, AppContext, ClipboardItem, - Element, ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, View, ViewContext, - WeakViewHandle, + fonts::Properties as FontProperties, geometry::vector::Vector2F, keymap::Binding, text_layout, + AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle, + MutableAppContext, TextLayoutCache, View, ViewContext, WeakViewHandle, }; -use gpui::{geometry::vector::Vector2F, TextLayoutCache}; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -2135,7 +2134,14 @@ impl workspace::ItemView for BufferView { Some(clone) } - fn save(&self, ctx: &mut ViewContext) -> LocalBoxFuture<'static, Result<()>> { + fn save( + &mut self, + file: Option, + ctx: &mut ViewContext, + ) -> LocalBoxFuture<'static, Result<()>> { + if file.is_some() { + self.file = file; + } if let Some(file) = self.file.as_ref() { self.buffer .update(ctx, |buffer, ctx| buffer.save(file, ctx)) diff --git a/zed/src/menus.rs b/zed/src/menus.rs index 08afb1e990c413f2fc89fae3d820d9c1d56dc462..8def5fafbac1ff246734a06819947571bff7db5e 100644 --- a/zed/src/menus.rs +++ b/zed/src/menus.rs @@ -24,12 +24,21 @@ pub fn menus(settings: Receiver) -> Vec> { }, Menu { name: "File", - items: vec![MenuItem::Action { - name: "Open…", - keystroke: Some("cmd-o"), - action: "workspace:open", - arg: Some(Box::new(settings)), - }], + items: vec![ + MenuItem::Action { + name: "New", + keystroke: Some("cmd-n"), + action: "workspace:new_file", + arg: None, + }, + MenuItem::Separator, + MenuItem::Action { + name: "Open…", + keystroke: Some("cmd-o"), + action: "workspace:open", + arg: Some(Box::new(settings)), + }, + ], }, Menu { name: "Edit", diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 629d23beecce0bdf1e1c59a76cd6fbe465f92f9b..50864a874bca59cbc6d8b937e9d4ba787ff95275 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -6,6 +6,7 @@ pub use pane_group::*; use crate::{ settings::Settings, watch::{self, Receiver}, + worktree::FileHandle, }; use gpui::{MutableAppContext, PathPromptOptions}; use std::path::PathBuf; @@ -15,6 +16,7 @@ pub fn init(app: &mut MutableAppContext) { app.add_global_action("app:quit", quit); app.add_action("workspace:save", Workspace::save_active_item); app.add_action("workspace:debug_elements", Workspace::debug_elements); + app.add_action("workspace:new_file", Workspace::open_new_file); app.add_bindings(vec![ Binding::new("cmd-s", "workspace:save", None), Binding::new("cmd-alt-i", "workspace:debug_elements", None), @@ -108,7 +110,11 @@ pub trait ItemView: View { fn is_dirty(&self, _: &AppContext) -> bool { false } - fn save(&self, _: &mut ViewContext) -> LocalBoxFuture<'static, anyhow::Result<()>> { + fn save( + &mut self, + _: Option, + _: &mut ViewContext, + ) -> LocalBoxFuture<'static, anyhow::Result<()>> { Box::pin(async { Ok(()) }) } fn should_activate_item_on_event(_: &Self::Event) -> bool { @@ -128,7 +134,11 @@ pub trait ItemViewHandle: Send + Sync { fn id(&self) -> usize; fn to_any(&self) -> AnyViewHandle; fn is_dirty(&self, ctx: &AppContext) -> bool; - fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>>; + fn save( + &self, + file: Option, + ctx: &mut MutableAppContext, + ) -> LocalBoxFuture<'static, anyhow::Result<()>>; } impl ItemViewHandle for ViewHandle { @@ -167,8 +177,12 @@ impl ItemViewHandle for ViewHandle { }) } - fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>> { - self.update(ctx, |item, ctx| item.save(ctx)) + fn save( + &self, + file: Option, + ctx: &mut MutableAppContext, + ) -> LocalBoxFuture<'static, anyhow::Result<()>> { + self.update(ctx, |item, ctx| item.save(file, ctx)) } fn is_dirty(&self, ctx: &AppContext) -> bool { @@ -209,6 +223,7 @@ pub struct Workspace { (usize, u64), postage::watch::Receiver, Arc>>>, >, + untitled_buffers: HashSet>, } impl Workspace { @@ -234,6 +249,7 @@ impl Workspace { replica_id, worktrees: Default::default(), buffers: Default::default(), + untitled_buffers: Default::default(), } } @@ -272,15 +288,7 @@ impl Workspace { let entries = paths .iter() .cloned() - .map(|path| { - for tree in self.worktrees.iter() { - if let Ok(relative_path) = path.strip_prefix(tree.read(ctx).abs_path()) { - return (tree.id(), relative_path.into()); - } - } - let worktree_id = self.add_worktree(&path, ctx); - (worktree_id, Path::new("").into()) - }) + .map(|path| self.file_for_path(&path, ctx)) .collect::>(); let bg = ctx.background_executor().clone(); @@ -288,12 +296,12 @@ impl Workspace { .iter() .cloned() .zip(entries.into_iter()) - .map(|(path, entry)| { + .map(|(abs_path, file)| { ctx.spawn( - bg.spawn(async move { path.is_file() }), - |me, is_file, ctx| { + bg.spawn(async move { abs_path.is_file() }), + move |me, is_file, ctx| { if is_file { - me.open_entry(entry, ctx) + me.open_entry(file.entry_id(), ctx) } else { None } @@ -310,13 +318,26 @@ impl Workspace { } } - pub fn add_worktree(&mut self, path: &Path, ctx: &mut ViewContext) -> usize { + fn file_for_path(&mut self, abs_path: &Path, ctx: &mut ViewContext) -> FileHandle { + for tree in self.worktrees.iter() { + if let Ok(relative_path) = abs_path.strip_prefix(tree.read(ctx).abs_path()) { + return tree.file(relative_path, ctx.as_ref()); + } + } + let worktree = self.add_worktree(&abs_path, ctx); + worktree.file(Path::new(""), ctx.as_ref()) + } + + pub fn add_worktree( + &mut self, + path: &Path, + ctx: &mut ViewContext, + ) -> ModelHandle { let worktree = ctx.add_model(|ctx| Worktree::new(path, ctx)); - let worktree_id = worktree.id(); ctx.observe_model(&worktree, |_, _, ctx| ctx.notify()); - self.worktrees.insert(worktree); + self.worktrees.insert(worktree.clone()); ctx.notify(); - worktree_id + worktree } pub fn toggle_modal(&mut self, ctx: &mut ViewContext, add_view: F) @@ -346,6 +367,15 @@ impl Workspace { } } + pub fn open_new_file(&mut self, _: &(), ctx: &mut ViewContext) { + let buffer = ctx.add_model(|_| Buffer::new(self.replica_id, "")); + let buffer_view = Box::new(ctx.add_view(|ctx| { + BufferView::for_buffer(buffer.clone(), None, self.settings.clone(), ctx) + })); + self.untitled_buffers.insert(buffer); + self.add_item(buffer_view, ctx); + } + #[must_use] pub fn open_entry( &mut self, @@ -381,13 +411,11 @@ impl Workspace { } }; - let file = match worktree.file(path.clone(), ctx.as_ref()) { - Some(file) => file, - None => { - log::error!("path {:?} does not exist", path); - return None; - } - }; + let file = worktree.file(path.clone(), ctx.as_ref()); + if file.is_deleted() { + log::error!("path {:?} does not exist", path); + return None; + } self.loading_entries.insert(entry.clone()); @@ -441,12 +469,34 @@ impl Workspace { } pub fn save_active_item(&mut self, _: &(), ctx: &mut ViewContext) { - self.active_pane.update(ctx, |pane, ctx| { + let handle = ctx.handle(); + let first_worktree = self.worktrees.iter().next(); + self.active_pane.update(ctx, move |pane, ctx| { if let Some(item) = pane.active_item() { - let task = item.save(ctx.as_mut()); + if item.entry_id(ctx.as_ref()).is_none() { + let start_path = first_worktree + .map_or(Path::new(""), |h| h.read(ctx).abs_path()) + .to_path_buf(); + ctx.prompt_for_new_path(&start_path, move |path, ctx| { + if let Some(path) = path { + handle.update(ctx, move |this, ctx| { + let file = this.file_for_path(&path, ctx); + let task = item.save(Some(file), ctx.as_mut()); + ctx.spawn(task, |_, result, _| { + if let Err(e) = result { + error!("failed to save item: {:?}, ", e); + } + }) + .detach() + }) + } + }); + return; + } + + let task = item.save(None, ctx.as_mut()); ctx.spawn(task, |_, result, _| { if let Err(e) = result { - // TODO - present this error to the user error!("failed to save item: {:?}, ", e); } }) diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index ea023fb813d49f2260abbea835aa97b07a33470e..b7fbdb3544ddbdf49d8a8c00f65d4dca3a4eb8df 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -1126,31 +1126,38 @@ struct UpdateIgnoreStatusJob { } pub trait WorktreeHandle { - fn file(&self, path: impl AsRef, app: &AppContext) -> Option; + fn file(&self, path: impl AsRef, app: &AppContext) -> FileHandle; } impl WorktreeHandle for ModelHandle { - fn file(&self, path: impl AsRef, app: &AppContext) -> Option { + fn file(&self, path: impl AsRef, app: &AppContext) -> FileHandle { + let path = path.as_ref(); let tree = self.read(app); - let entry = tree.entry_for_path(&path)?; - - let path = entry.path().clone(); let mut handles = tree.handles.lock(); - let state = if let Some(state) = handles.get(&path).and_then(Weak::upgrade) { + let state = if let Some(state) = handles.get(path).and_then(Weak::upgrade) { state } else { - let state = Arc::new(Mutex::new(FileHandleState { - path: path.clone(), - is_deleted: false, - })); - handles.insert(path, Arc::downgrade(&state)); + let handle_state = if let Some(entry) = tree.entry_for_path(path) { + FileHandleState { + path: entry.path().clone(), + is_deleted: false, + } + } else { + FileHandleState { + path: path.into(), + is_deleted: true, + } + }; + + let state = Arc::new(Mutex::new(handle_state.clone())); + handles.insert(handle_state.path, Arc::downgrade(&state)); state }; - Some(FileHandle { + FileHandle { worktree: self.clone(), state, - }) + } } } @@ -1389,10 +1396,10 @@ mod tests { let (file2, file3, file4, file5) = app.read(|ctx| { ( - tree.file("a/file2", ctx).unwrap(), - tree.file("a/file3", ctx).unwrap(), - tree.file("b/c/file4", ctx).unwrap(), - tree.file("b/c/file5", ctx).unwrap(), + tree.file("a/file2", ctx), + tree.file("a/file3", ctx), + tree.file("b/c/file4", ctx), + tree.file("b/c/file5", ctx), ) }); From 1fcbadaa990137b38b6e6bddcb352584ef7984ae Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 May 2021 11:04:39 -0700 Subject: [PATCH 02/27] Add TestAppContext::simulate_new_path_selection --- gpui/src/app.rs | 21 ++++++++++++++------- gpui/src/platform/test.rs | 33 +++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 75d0ae01462be12ede526938f4126111c20ab076..798805f63ebb98923956d5ea73b2b558706e3545 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -87,7 +87,7 @@ pub enum MenuItem<'a> { pub struct App(Rc>); #[derive(Clone)] -pub struct TestAppContext(Rc>); +pub struct TestAppContext(Rc>, Rc); impl App { pub fn test T>( @@ -111,13 +111,16 @@ impl App { Fn: FnOnce(TestAppContext) -> F, F: Future, { - let platform = platform::test::platform(); + let platform = Rc::new(platform::test::platform()); let foreground = Rc::new(executor::Foreground::test()); - let ctx = TestAppContext(Rc::new(RefCell::new(MutableAppContext::new( - foreground.clone(), - Rc::new(platform), - asset_source, - )))); + let ctx = TestAppContext( + Rc::new(RefCell::new(MutableAppContext::new( + foreground.clone(), + platform.clone(), + asset_source, + ))), + platform, + ); ctx.0.borrow_mut().weak_self = Some(Rc::downgrade(&ctx.0)); let future = f(ctx); @@ -332,6 +335,10 @@ impl TestAppContext { pub fn platform(&self) -> Rc { self.0.borrow().platform.clone() } + + pub fn simulate_new_path_selection(&self, result: impl FnOnce(PathBuf) -> Option) { + self.1.as_ref().simulate_new_path_selection(result); + } } impl UpdateModel for TestAppContext { diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index de385bbf844a0493579500a18aee8726bd34c608..6ee772c7e05467911dda82ef705ad512ada5b1da 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -1,11 +1,18 @@ use crate::ClipboardItem; use pathfinder_geometry::vector::Vector2F; -use std::{any::Any, cell::RefCell, path::Path, rc::Rc, sync::Arc}; - -struct Platform { +use std::{ + any::Any, + cell::RefCell, + path::{Path, PathBuf}, + rc::Rc, + sync::Arc, +}; + +pub(crate) struct Platform { dispatcher: Arc, fonts: Arc, current_clipboard_item: RefCell>, + last_prompt_for_new_path_args: RefCell)>)>>, } struct Dispatcher; @@ -23,9 +30,21 @@ impl Platform { Self { dispatcher: Arc::new(Dispatcher), fonts: Arc::new(super::current::FontSystem::new()), - current_clipboard_item: RefCell::new(None), + current_clipboard_item: Default::default(), + last_prompt_for_new_path_args: Default::default(), } } + + pub(crate) fn simulate_new_path_selection( + &self, + result: impl FnOnce(PathBuf) -> Option, + ) { + let (dir_path, callback) = self + .last_prompt_for_new_path_args + .take() + .expect("prompt_for_new_path was not called"); + callback(result(dir_path)); + } } impl super::Platform for Platform { @@ -77,7 +96,9 @@ impl super::Platform for Platform { ) { } - fn prompt_for_new_path(&self, _: &Path, _: Box)>) {} + fn prompt_for_new_path(&self, path: &Path, f: Box)>) { + *self.last_prompt_for_new_path_args.borrow_mut() = Some((path.to_path_buf(), f)); + } fn write_to_clipboard(&self, item: ClipboardItem) { *self.current_clipboard_item.borrow_mut() = Some(item); @@ -134,6 +155,6 @@ impl super::Window for Window { } } -pub fn platform() -> impl super::Platform { +pub(crate) fn platform() -> Platform { Platform::new() } From 47d03498f482e9ef2fdb29f458730bfafed96cb7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 May 2021 11:05:50 -0700 Subject: [PATCH 03/27] Wait to update bufferview's file handle until save has completed --- zed/src/editor/buffer/mod.rs | 3 +- zed/src/editor/buffer_view.rs | 21 +++--- zed/src/workspace.rs | 116 ++++++++++++++++++++++++---------- 3 files changed, 96 insertions(+), 44 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 59b0bf9b36cee6bab3d03b00e3212d81b85fcd9e..fc0de35ca413609c509cbf94b3220b1af487805b 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -3123,8 +3123,7 @@ mod tests { let mut buffers = Vec::new(); let mut network = Network::new(); for i in 0..PEERS { - let buffer = - ctx.add_model(|_| Buffer::new(i as ReplicaId, base_text.as_str())); + let buffer = 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); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 58e4428d6004e3d1b31d4151be646b8510fea077..27cb9b4be5fa505cc7d0bd94330b7149ecbc42dd 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -13,7 +13,7 @@ use gpui::{ use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; -use smol::Timer; +use smol::{future::FutureExt, Timer}; use std::{ cmp::{self, Ordering}, fmt::Write, @@ -2136,15 +2136,20 @@ impl workspace::ItemView for BufferView { fn save( &mut self, - file: Option, + new_file: Option, ctx: &mut ViewContext, ) -> LocalBoxFuture<'static, Result<()>> { - if file.is_some() { - self.file = file; - } - if let Some(file) = self.file.as_ref() { - self.buffer - .update(ctx, |buffer, ctx| buffer.save(file, ctx)) + if let Some(file) = new_file.as_ref().or(self.file.as_ref()) { + let save = self.buffer.update(ctx, |b, ctx| b.save(file, ctx)); + ctx.spawn(save, move |this, result, ctx| { + if new_file.is_some() && result.is_ok() { + this.file = new_file; + ctx.emit(Event::FileHandleChanged); + ctx.notify(); + } + result + }) + .boxed_local() } else { Box::pin(async { Ok(()) }) } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 50864a874bca59cbc6d8b937e9d4ba787ff95275..5d570a845a9a6086395e722b607536ccc4960ab6 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -369,11 +369,11 @@ impl Workspace { pub fn open_new_file(&mut self, _: &(), ctx: &mut ViewContext) { let buffer = ctx.add_model(|_| Buffer::new(self.replica_id, "")); - let buffer_view = Box::new(ctx.add_view(|ctx| { + let buffer_view = ctx.add_view(|ctx| { BufferView::for_buffer(buffer.clone(), None, self.settings.clone(), ctx) - })); + }); self.untitled_buffers.insert(buffer); - self.add_item(buffer_view, ctx); + self.add_item(Box::new(buffer_view), ctx); } #[must_use] @@ -468,41 +468,45 @@ impl Workspace { )) } + pub fn active_item(&self, ctx: &ViewContext) -> Option> { + self.active_pane().read(ctx).active_item() + } + pub fn save_active_item(&mut self, _: &(), ctx: &mut ViewContext) { - let handle = ctx.handle(); - let first_worktree = self.worktrees.iter().next(); - self.active_pane.update(ctx, move |pane, ctx| { - if let Some(item) = pane.active_item() { - if item.entry_id(ctx.as_ref()).is_none() { - let start_path = first_worktree - .map_or(Path::new(""), |h| h.read(ctx).abs_path()) - .to_path_buf(); - ctx.prompt_for_new_path(&start_path, move |path, ctx| { - if let Some(path) = path { - handle.update(ctx, move |this, ctx| { - let file = this.file_for_path(&path, ctx); - let task = item.save(Some(file), ctx.as_mut()); - ctx.spawn(task, |_, result, _| { - if let Err(e) = result { - error!("failed to save item: {:?}, ", e); - } - }) - .detach() + if let Some(item) = self.active_item(ctx) { + if item.entry_id(ctx.as_ref()).is_none() { + let handle = ctx.handle(); + let start_path = self + .worktrees + .iter() + .next() + .map_or(Path::new(""), |h| h.read(ctx).abs_path()) + .to_path_buf(); + ctx.prompt_for_new_path(&start_path, move |path, ctx| { + if let Some(path) = path { + handle.update(ctx, move |this, ctx| { + let file = this.file_for_path(&path, ctx); + let task = item.save(Some(file), ctx.as_mut()); + ctx.spawn(task, |_, result, _| { + if let Err(e) = result { + error!("failed to save item: {:?}, ", e); + } }) - } - }); - return; - } - - let task = item.save(None, ctx.as_mut()); - ctx.spawn(task, |_, result, _| { - if let Err(e) = result { - error!("failed to save item: {:?}, ", e); + .detach() + }) } - }) - .detach() + }); + return; } - }); + + let task = item.save(None, ctx.as_mut()); + ctx.spawn(task, |_, result, _| { + if let Err(e) = result { + error!("failed to save item: {:?}, ", e); + } + }) + .detach() + } } pub fn debug_elements(&mut self, _: &(), ctx: &mut ViewContext) { @@ -660,6 +664,7 @@ mod tests { use gpui::App; use serde_json::json; use std::{collections::HashSet, os::unix}; + use tempdir::TempDir; #[test] fn test_open_paths_action() { @@ -942,6 +947,49 @@ mod tests { }); } + #[test] + fn test_open_and_save_new_file() { + App::test_async((), |mut app| async move { + let dir = TempDir::new("test-new-file").unwrap(); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(dir.path(), ctx); + workspace + }); + + // Create a new untitled buffer + let editor = workspace.update(&mut app, |workspace, ctx| { + workspace.open_new_file(&(), ctx); + workspace + .active_item(ctx) + .unwrap() + .to_any() + .downcast::() + .unwrap() + }); + editor.update(&mut app, |editor, ctx| { + assert_eq!(editor.title(ctx.as_ref()), "untitled"); + editor.insert(&"hi".to_string(), ctx) + }); + + // Save the buffer, selecting a filename + workspace.update(&mut app, |workspace, ctx| { + workspace.save_active_item(&(), ctx) + }); + app.simulate_new_path_selection(|parent_dir| { + assert_eq!(parent_dir, dir.path()); + Some(parent_dir.join("the-new-name")) + }); + app.read(|ctx| assert_eq!(editor.title(ctx), "untitled")); + + // When the save completes, the buffer's title is updated. + editor + .condition(&app, |editor, ctx| editor.title(ctx) == "the-new-name") + .await; + }); + } + #[test] fn test_pane_actions() { App::test_async((), |mut app| async move { From 78a5d0a37873ba2462a0c3c86d0a0d5d6a472ba5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 May 2021 13:12:51 -0700 Subject: [PATCH 04/27] Maintain workspace buffers state after saving untitled buffer --- zed/src/editor/buffer/mod.rs | 2 +- zed/src/editor/buffer_view.rs | 8 +++-- zed/src/workspace.rs | 67 +++++++++++++++++++++++++++++------ zed/src/worktree.rs | 20 +++++++---- 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index fc0de35ca413609c509cbf94b3220b1af487805b..b1cf5a09575bd64f20a04521e9c9d277f5effed2 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -436,7 +436,7 @@ impl Buffer { &mut self, file: &FileHandle, ctx: &mut ModelContext, - ) -> LocalBoxFuture<'static, Result<()>> { + ) -> LocalBoxFuture<'static, Result> { let snapshot = self.snapshot(); let version = self.version.clone(); let save_task = file.save(snapshot, ctx.as_ref()); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 27cb9b4be5fa505cc7d0bd94330b7149ecbc42dd..15ce63120ef6832966d3bbbf15c77ff8286a1aee 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -2058,6 +2058,10 @@ impl BufferView { buffer::Event::FileHandleChanged => ctx.emit(Event::FileHandleChanged), } } + + pub fn file(&self) -> Option<&FileHandle> { + self.file.as_ref() + } } pub enum Event { @@ -2138,7 +2142,7 @@ impl workspace::ItemView for BufferView { &mut self, new_file: Option, ctx: &mut ViewContext, - ) -> LocalBoxFuture<'static, Result<()>> { + ) -> LocalBoxFuture<'static, Result> { if let Some(file) = new_file.as_ref().or(self.file.as_ref()) { let save = self.buffer.update(ctx, |b, ctx| b.save(file, ctx)); ctx.spawn(save, move |this, result, ctx| { @@ -2151,7 +2155,7 @@ impl workspace::ItemView for BufferView { }) .boxed_local() } else { - Box::pin(async { Ok(()) }) + Box::pin(async { Err(anyhow::anyhow!("can't save a buffer with no file")) }) } } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 5d570a845a9a6086395e722b607536ccc4960ab6..24b6d8447b3be4593a9f2b688aeb60ffc51c4ec5 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -114,9 +114,7 @@ pub trait ItemView: View { &mut self, _: Option, _: &mut ViewContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>> { - Box::pin(async { Ok(()) }) - } + ) -> LocalBoxFuture<'static, anyhow::Result>; fn should_activate_item_on_event(_: &Self::Event) -> bool { false } @@ -138,7 +136,7 @@ pub trait ItemViewHandle: Send + Sync { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>>; + ) -> LocalBoxFuture<'static, anyhow::Result>; } impl ItemViewHandle for ViewHandle { @@ -181,7 +179,7 @@ impl ItemViewHandle for ViewHandle { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>> { + ) -> LocalBoxFuture<'static, anyhow::Result> { self.update(ctx, |item, ctx| item.save(file, ctx)) } @@ -223,7 +221,7 @@ pub struct Workspace { (usize, u64), postage::watch::Receiver, Arc>>>, >, - untitled_buffers: HashSet>, + untitled_buffers: HashMap>, } impl Workspace { @@ -372,7 +370,7 @@ impl Workspace { let buffer_view = ctx.add_view(|ctx| { BufferView::for_buffer(buffer.clone(), None, self.settings.clone(), ctx) }); - self.untitled_buffers.insert(buffer); + self.untitled_buffers.insert(buffer_view.id(), buffer); self.add_item(Box::new(buffer_view), ctx); } @@ -486,11 +484,20 @@ impl Workspace { if let Some(path) = path { handle.update(ctx, move |this, ctx| { let file = this.file_for_path(&path, ctx); + let worktree_id = file.worktree_id(); let task = item.save(Some(file), ctx.as_mut()); - ctx.spawn(task, |_, result, _| { - if let Err(e) = result { + let item_id = item.id(); + ctx.spawn(task, move |this, result, _| match result { + Err(e) => { error!("failed to save item: {:?}, ", e); } + Ok(inode) => { + if let Some(buffer) = this.untitled_buffers.remove(&item_id) { + let (_, rx) = + postage::watch::channel_with(Some(Ok(buffer))); + this.buffers.insert((worktree_id, inode), rx); + } + } }) .detach() }) @@ -984,9 +991,47 @@ mod tests { app.read(|ctx| assert_eq!(editor.title(ctx), "untitled")); // When the save completes, the buffer's title is updated. - editor - .condition(&app, |editor, ctx| editor.title(ctx) == "the-new-name") + let worktree = app.read(|ctx| { + workspace + .read(ctx) + .worktrees() + .iter() + .next() + .unwrap() + .clone() + }); + worktree + .condition(&app, |worktree, _| { + worktree.inode_for_path("the-new-name").is_some() + }) .await; + + // Open the same newly-created file in another pane item. + // The new editor should reuse the same buffer. + workspace + .update(&mut app, |workspace, ctx| { + workspace.open_new_file(&(), ctx); + workspace.split_pane( + workspace.active_pane().clone(), + SplitDirection::Right, + ctx, + ); + workspace + .open_entry((worktree.id(), Path::new("the-new-name").into()), ctx) + .unwrap() + }) + .await; + let editor2 = workspace.update(&mut app, |workspace, ctx| { + workspace + .active_item(ctx) + .unwrap() + .to_any() + .downcast::() + .unwrap() + }); + app.read(|ctx| { + assert_eq!(editor.read(ctx).buffer(), editor2.read(ctx).buffer()); + }) }); } diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index b7fbdb3544ddbdf49d8a8c00f65d4dca3a4eb8df..00900cdb9d35b0ba6c6f8ca1939868d8e2eb0b51 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -191,17 +191,18 @@ impl Worktree { path: &Path, content: BufferSnapshot, ctx: &AppContext, - ) -> Task> { + ) -> Task> { let abs_path = self.snapshot.abs_path.join(path); ctx.background_executor().spawn(async move { let buffer_size = content.text_summary().bytes.min(10 * 1024); let file = std::fs::File::create(&abs_path)?; + let metadata = file.metadata()?; let mut writer = std::io::BufWriter::with_capacity(buffer_size, file); for chunk in content.fragments() { writer.write(chunk.as_bytes())?; } writer.flush()?; - Ok(()) + Ok(metadata.ino()) }) } } @@ -406,15 +407,23 @@ impl FileHandle { self.state.lock().is_deleted } + pub fn exists(&self) -> bool { + !self.is_deleted() + } + pub fn load_history(&self, ctx: &AppContext) -> impl Future> { self.worktree.read(ctx).load_history(&self.path(), ctx) } - pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { + pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { let worktree = self.worktree.read(ctx); worktree.save(&self.path(), content, ctx) } + pub fn worktree_id(&self) -> usize { + self.worktree.id() + } + pub fn entry_id(&self) -> (usize, Arc) { (self.worktree.id(), self.path()) } @@ -971,9 +980,8 @@ impl BackgroundScanner { let snapshot = self.snapshot.lock(); handles.retain(|path, handle_state| { if let Some(handle_state) = Weak::upgrade(&handle_state) { - if snapshot.entry_for_path(&path).is_none() { - handle_state.lock().is_deleted = true; - } + let mut handle_state = handle_state.lock(); + handle_state.is_deleted = snapshot.entry_for_path(&path).is_none(); true } else { false From bf28aba1a79e48bbb8cf75173a65208deb20022c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 May 2021 17:23:44 -0700 Subject: [PATCH 05/27] Restore Buffer::file field and workspace::Item trait --- zed/src/editor/buffer/mod.rs | 121 +++++++----- zed/src/editor/buffer_view.rs | 146 +++++++-------- zed/src/editor/display_map/fold_map.rs | 14 +- zed/src/editor/display_map/mod.rs | 4 +- zed/src/workspace.rs | 243 +++++++++++++++---------- zed/src/worktree.rs | 22 +-- 6 files changed, 319 insertions(+), 231 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index b1cf5a09575bd64f20a04521e9c9d277f5effed2..6d87dd588019ca5db889a6cff883830216f9db8d 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -8,6 +8,7 @@ use futures_core::future::LocalBoxFuture; pub use point::*; use seahash::SeaHasher; pub use selection::*; +use smol::future::FutureExt; pub use text::*; use crate::{ @@ -64,6 +65,7 @@ pub struct Buffer { last_edit: time::Local, undo_map: UndoMap, history: History, + file: Option, selections: HashMap>, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, @@ -351,15 +353,33 @@ pub struct UndoOperation { } impl Buffer { - pub fn new>>(replica_id: ReplicaId, base_text: T) -> Self { - Self::build(replica_id, History::new(base_text.into())) + pub fn new>>( + replica_id: ReplicaId, + base_text: T, + ctx: &mut ModelContext, + ) -> Self { + Self::build(replica_id, History::new(base_text.into()), None, ctx) } - pub fn from_history(replica_id: ReplicaId, history: History) -> Self { - Self::build(replica_id, history) + pub fn from_history( + replica_id: ReplicaId, + history: History, + file: Option, + ctx: &mut ModelContext, + ) -> Self { + Self::build(replica_id, history, file, ctx) } - fn build(replica_id: ReplicaId, history: History) -> Self { + fn build( + replica_id: ReplicaId, + history: History, + file: Option, + ctx: &mut ModelContext, + ) -> Self { + if let Some(file) = file.as_ref() { + file.observe_from_model(ctx, |_, _, ctx| ctx.emit(Event::FileHandleChanged)); + } + let mut insertion_splits = HashMap::default(); let mut fragments = SumTree::new(); @@ -416,6 +436,7 @@ impl Buffer { last_edit: time::Local::default(), undo_map: Default::default(), history, + file, selections: HashMap::default(), selections_last_update: 0, deferred_ops: OperationQueue::new(), @@ -432,24 +453,38 @@ impl Buffer { } } + pub fn file(&self) -> Option<&FileHandle> { + self.file.as_ref() + } + pub fn save( &mut self, - file: &FileHandle, + new_file: Option, ctx: &mut ModelContext, - ) -> LocalBoxFuture<'static, Result> { + ) -> LocalBoxFuture<'static, Result<()>> { let snapshot = self.snapshot(); let version = self.version.clone(); - let save_task = file.save(snapshot, ctx.as_ref()); - let task = ctx.spawn(save_task, |me, save_result, ctx| { - if save_result.is_ok() { - me.did_save(version, ctx); - } - save_result - }); - Box::pin(task) + if let Some(file) = new_file.as_ref().or(self.file.as_ref()) { + let save_task = file.save(snapshot, ctx.as_ref()); + ctx.spawn(save_task, |me, save_result, ctx| { + if save_result.is_ok() { + me.did_save(version, new_file, ctx); + } + save_result + }) + .boxed_local() + } else { + async { Ok(()) }.boxed_local() + } } - fn did_save(&mut self, version: time::Global, ctx: &mut ModelContext) { + fn did_save( + &mut self, + version: time::Global, + file: Option, + ctx: &mut ModelContext, + ) { + self.file = file; self.saved_version = version; ctx.emit(Event::Saved); } @@ -1737,6 +1772,7 @@ impl Clone for Buffer { selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), + file: self.file.clone(), deferred_replicas: self.deferred_replicas.clone(), replica_id: self.replica_id, local_clock: self.local_clock.clone(), @@ -2296,8 +2332,8 @@ mod tests { #[test] fn test_edit() { App::test((), |ctx| { - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, "abc"); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "abc", ctx); assert_eq!(buffer.text(), "abc"); buffer.edit(vec![3..3], "def", None).unwrap(); assert_eq!(buffer.text(), "abcdef"); @@ -2321,8 +2357,8 @@ mod tests { let buffer_1_events = Rc::new(RefCell::new(Vec::new())); let buffer_2_events = Rc::new(RefCell::new(Vec::new())); - let buffer1 = app.add_model(|_| Buffer::new(0, "abcdef")); - let buffer2 = app.add_model(|_| Buffer::new(1, "abcdef")); + let buffer1 = app.add_model(|ctx| Buffer::new(0, "abcdef", ctx)); + let buffer2 = app.add_model(|ctx| Buffer::new(1, "abcdef", ctx)); let mut buffer_ops = Vec::new(); buffer1.update(app, |buffer, ctx| { let buffer_1_events = buffer_1_events.clone(); @@ -2408,8 +2444,8 @@ mod tests { let mut reference_string = RandomCharIter::new(&mut rng) .take(reference_string_len) .collect::(); - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, reference_string.as_str()); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, reference_string.as_str(), ctx); let mut buffer_versions = Vec::new(); for _i in 0..10 { let (old_ranges, new_text, _) = buffer.randomly_mutate(rng, None); @@ -2494,8 +2530,8 @@ mod tests { #[test] fn test_line_len() { App::test((), |ctx| { - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, ""); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap(); buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); buffer.edit(vec![18..18], "\npqrs\n", None).unwrap(); @@ -2516,8 +2552,8 @@ mod tests { #[test] fn test_rightmost_point() { App::test((), |ctx| { - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, ""); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); assert_eq!(buffer.rightmost_point().row, 0); buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap(); assert_eq!(buffer.rightmost_point().row, 0); @@ -2537,8 +2573,8 @@ mod tests { #[test] fn test_text_summary_for_range() { App::test((), |ctx| { - ctx.add_model(|_| { - let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz"); + ctx.add_model(|ctx| { + let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", ctx); let text = Text::from(buffer.text()); assert_eq!( buffer.text_summary_for_range(1..3), @@ -2568,8 +2604,8 @@ mod tests { #[test] fn test_chars_at() { App::test((), |ctx| { - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, ""); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); buffer.edit(vec![0..0], "abcd\nefgh\nij", None).unwrap(); buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); buffer.edit(vec![18..18], "\npqrs", None).unwrap(); @@ -2591,7 +2627,7 @@ mod tests { assert_eq!(chars.collect::(), "PQrs"); // Regression test: - let mut buffer = Buffer::new(0, ""); + let mut buffer = Buffer::new(0, "", ctx); buffer.edit(vec![0..0], "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n", None).unwrap(); buffer.edit(vec![60..60], "\n", None).unwrap(); @@ -2720,8 +2756,8 @@ mod tests { #[test] fn test_anchors() { App::test((), |ctx| { - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, ""); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); buffer.edit(vec![0..0], "abc", None).unwrap(); let left_anchor = buffer.anchor_before(2).unwrap(); let right_anchor = buffer.anchor_after(2).unwrap(); @@ -2885,8 +2921,8 @@ mod tests { #[test] fn test_anchors_at_start_and_end() { App::test((), |ctx| { - ctx.add_model(|_| { - let mut buffer = Buffer::new(0, ""); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); let before_start_anchor = buffer.anchor_before(0).unwrap(); let after_end_anchor = buffer.anchor_after(0).unwrap(); @@ -2913,7 +2949,7 @@ mod tests { #[test] fn test_is_modified() { App::test((), |app| { - let model = app.add_model(|_| Buffer::new(0, "abc")); + let model = app.add_model(|ctx| Buffer::new(0, "abc", ctx)); let events = Rc::new(RefCell::new(Vec::new())); // initially, the buffer isn't dirty. @@ -2945,7 +2981,7 @@ mod tests { ); events.borrow_mut().clear(); - buffer.did_save(buffer.version(), ctx); + buffer.did_save(buffer.version(), None, ctx); }); // after saving, the buffer is not dirty, and emits a saved event. @@ -3000,8 +3036,8 @@ mod tests { #[test] fn test_undo_redo() { App::test((), |app| { - app.add_model(|_| { - let mut buffer = Buffer::new(0, "1234"); + app.add_model(|ctx| { + let mut buffer = Buffer::new(0, "1234", ctx); let edit1 = buffer.edit(vec![1..1], "abx", None).unwrap(); let edit2 = buffer.edit(vec![3..4], "yzef", None).unwrap(); @@ -3037,9 +3073,9 @@ mod tests { #[test] fn test_history() { App::test((), |app| { - app.add_model(|_| { + app.add_model(|ctx| { let mut now = Instant::now(); - let mut buffer = Buffer::new(0, "123456"); + let mut buffer = Buffer::new(0, "123456", ctx); let (set_id, _) = buffer .add_selection_set(buffer.selections_from_ranges(vec![4..4]).unwrap(), None); @@ -3123,7 +3159,8 @@ mod tests { let mut buffers = Vec::new(); let mut network = Network::new(); for i in 0..PEERS { - let buffer = ctx.add_model(|_| Buffer::new(i as ReplicaId, base_text.as_str())); + let buffer = + ctx.add_model(|ctx| Buffer::new(i as ReplicaId, base_text.as_str(), ctx)); buffers.push(buffer); replica_ids.push(i as u16); network.add_peer(i as u16); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 15ce63120ef6832966d3bbbf15c77ff8286a1aee..4fc58fb67324abf9d763efcc7709b6c0360f919a 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -13,7 +13,7 @@ use gpui::{ use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; -use smol::{future::FutureExt, Timer}; +use smol::Timer; use std::{ cmp::{self, Ordering}, fmt::Write, @@ -253,7 +253,6 @@ pub enum SelectAction { pub struct BufferView { handle: WeakViewHandle, buffer: ModelHandle, - file: Option, display_map: ModelHandle, selection_set_id: SelectionSetId, pending_selection: Option, @@ -275,24 +274,19 @@ struct ClipboardSelection { impl BufferView { pub fn single_line(settings: watch::Receiver, ctx: &mut ViewContext) -> Self { - let buffer = ctx.add_model(|_| Buffer::new(0, String::new())); - let mut view = Self::for_buffer(buffer, None, settings, ctx); + let buffer = ctx.add_model(|ctx| Buffer::new(0, String::new(), ctx)); + let mut view = Self::for_buffer(buffer, settings, ctx); view.single_line = true; view } pub fn for_buffer( buffer: ModelHandle, - file: Option, settings: watch::Receiver, ctx: &mut ViewContext, ) -> Self { settings.notify_view_on_change(ctx); - if let Some(file) = file.as_ref() { - file.observe_from_view(ctx, |_, _, ctx| ctx.emit(Event::FileHandleChanged)); - } - ctx.observe_model(&buffer, Self::on_buffer_changed); ctx.subscribe_to_model(&buffer, Self::on_buffer_event); let display_map = ctx.add_model(|ctx| { @@ -318,7 +312,6 @@ impl BufferView { Self { handle: ctx.handle().downgrade(), buffer, - file, display_map, selection_set_id, pending_selection: None, @@ -2058,10 +2051,6 @@ impl BufferView { buffer::Event::FileHandleChanged => ctx.emit(Event::FileHandleChanged), } } - - pub fn file(&self) -> Option<&FileHandle> { - self.file.as_ref() - } } pub enum Event { @@ -2099,6 +2088,22 @@ impl View for BufferView { } } +impl workspace::Item for Buffer { + type View = BufferView; + + fn file(&self) -> Option<&FileHandle> { + self.file() + } + + fn build_view( + handle: ModelHandle, + settings: watch::Receiver, + ctx: &mut ViewContext, + ) -> Self::View { + BufferView::for_buffer(handle, settings, ctx) + } +} + impl workspace::ItemView for BufferView { fn should_activate_item_on_event(event: &Self::Event) -> bool { matches!(event, Event::Activate) @@ -2112,7 +2117,11 @@ impl workspace::ItemView for BufferView { } fn title(&self, app: &AppContext) -> std::string::String { - let filename = self.file.as_ref().and_then(|file| file.file_name(app)); + let filename = self + .buffer + .read(app) + .file() + .and_then(|file| file.file_name(app)); if let Some(name) = filename { name.to_string_lossy().into() } else { @@ -2120,20 +2129,15 @@ impl workspace::ItemView for BufferView { } } - fn entry_id(&self, _: &AppContext) -> Option<(usize, Arc)> { - self.file.as_ref().map(|file| file.entry_id()) + fn entry_id(&self, ctx: &AppContext) -> Option<(usize, Arc)> { + self.buffer.read(ctx).file().map(|file| file.entry_id()) } fn clone_on_split(&self, ctx: &mut ViewContext) -> Option where Self: Sized, { - let clone = BufferView::for_buffer( - self.buffer.clone(), - self.file.clone(), - self.settings.clone(), - ctx, - ); + let clone = BufferView::for_buffer(self.buffer.clone(), self.settings.clone(), ctx); *clone.scroll_position.lock() = *self.scroll_position.lock(); Some(clone) } @@ -2142,21 +2146,8 @@ impl workspace::ItemView for BufferView { &mut self, new_file: Option, ctx: &mut ViewContext, - ) -> LocalBoxFuture<'static, Result> { - if let Some(file) = new_file.as_ref().or(self.file.as_ref()) { - let save = self.buffer.update(ctx, |b, ctx| b.save(file, ctx)); - ctx.spawn(save, move |this, result, ctx| { - if new_file.is_some() && result.is_ok() { - this.file = new_file; - ctx.emit(Event::FileHandleChanged); - ctx.notify(); - } - result - }) - .boxed_local() - } else { - Box::pin(async { Err(anyhow::anyhow!("can't save a buffer with no file")) }) - } + ) -> LocalBoxFuture<'static, Result<()>> { + self.buffer.update(ctx, |b, ctx| b.save(new_file, ctx)) } fn is_dirty(&self, ctx: &AppContext) -> bool { @@ -2174,10 +2165,11 @@ mod tests { #[test] fn test_selection_with_mouse() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n")); + let buffer = + app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, buffer_view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); buffer_view.update(app, |view, ctx| { view.begin_selection(DisplayPoint::new(2, 2), false, ctx); @@ -2288,11 +2280,11 @@ mod tests { let layout_cache = TextLayoutCache::new(app.platform().fonts()); let font_cache = app.font_cache().clone(); - let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx)); let settings = settings::channel(&font_cache).unwrap().1; let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), None, settings, ctx)); + app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); let layouts = view .read(app) @@ -2305,7 +2297,7 @@ mod tests { #[test] fn test_fold() { App::test((), |app| { - let buffer = app.add_model(|_| { + let buffer = app.add_model(|ctx| { Buffer::new( 0, " @@ -2326,11 +2318,12 @@ mod tests { } " .unindent(), + ctx, ) }); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), None, settings, ctx)); + app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( @@ -2399,10 +2392,10 @@ mod tests { #[test] fn test_move_cursor() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx)); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), None, settings, ctx)); + app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); buffer.update(app, |buffer, ctx| { buffer @@ -2477,10 +2470,9 @@ mod tests { #[test] fn test_beginning_end_of_line() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, "abc\n def")); + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n def", ctx)); let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( &[ @@ -2606,11 +2598,10 @@ mod tests { #[test] fn test_prev_next_word_boundary() { App::test((), |app| { - let buffer = - app.add_model(|_| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}")); + let buffer = app + .add_model(|ctx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", ctx)); let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( &[ @@ -2789,12 +2780,16 @@ mod tests { #[test] fn test_backspace() { App::test((), |app| { - let buffer = app.add_model(|_| { - Buffer::new(0, "one two three\nfour five six\nseven eight nine\nten\n") + let buffer = app.add_model(|ctx| { + Buffer::new( + 0, + "one two three\nfour five six\nseven eight nine\nten\n", + ctx, + ) }); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), None, settings, ctx)); + app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( @@ -2822,12 +2817,16 @@ mod tests { #[test] fn test_delete() { App::test((), |app| { - let buffer = app.add_model(|_| { - Buffer::new(0, "one two three\nfour five six\nseven eight nine\nten\n") + let buffer = app.add_model(|ctx| { + Buffer::new( + 0, + "one two three\nfour five six\nseven eight nine\nten\n", + ctx, + ) }); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), None, settings, ctx)); + app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( @@ -2856,9 +2855,8 @@ mod tests { fn test_delete_line() { App::test((), |app| { let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|_| Buffer::new(0, "abc\ndef\nghi\n")); - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( &[ @@ -2881,9 +2879,8 @@ mod tests { ); let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|_| Buffer::new(0, "abc\ndef\nghi\n")); - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( &[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], @@ -2904,9 +2901,8 @@ mod tests { fn test_duplicate_line() { App::test((), |app| { let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|_| Buffer::new(0, "abc\ndef\nghi\n")); - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( &[ @@ -2935,9 +2931,8 @@ mod tests { ); let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|_| Buffer::new(0, "abc\ndef\nghi\n")); - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |view, ctx| { view.select_display_ranges( &[ @@ -2966,10 +2961,10 @@ mod tests { #[test] fn test_clipboard() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, "one two three four five six ")); + let buffer = app.add_model(|ctx| Buffer::new(0, "one two three four five six ", ctx)); let settings = settings::channel(&app.font_cache()).unwrap().1; let view = app - .add_window(|ctx| BufferView::for_buffer(buffer.clone(), None, settings, ctx)) + .add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)) .1; // Cut with three selections. Clipboard text is divided into three slices. @@ -3107,10 +3102,9 @@ mod tests { #[test] fn test_select_all() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, "abc\nde\nfgh")); + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\nde\nfgh", ctx)); let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); view.update(app, |b, ctx| b.select_all(&(), ctx)); assert_eq!( view.read(app).selection_ranges(app.as_ref()), diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 8a105f8af39d76ccebce43558ab9273ba50ae906..98c47ff08a84ea1a79ded4a0edf03da7ad0a681a 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -505,7 +505,7 @@ mod tests { #[test] fn test_basic_folds() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); let mut map = FoldMap::new(buffer.clone(), app.as_ref()); map.fold( @@ -556,7 +556,7 @@ mod tests { #[test] fn test_adjacent_folds() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, "abcdefghijkl")); + let buffer = app.add_model(|ctx| Buffer::new(0, "abcdefghijkl", ctx)); { let mut map = FoldMap::new(buffer.clone(), app.as_ref()); @@ -600,7 +600,7 @@ mod tests { #[test] fn test_overlapping_folds() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); let mut map = FoldMap::new(buffer.clone(), app.as_ref()); map.fold( vec![ @@ -619,7 +619,7 @@ mod tests { #[test] fn test_merging_folds_via_edit() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); let mut map = FoldMap::new(buffer.clone(), app.as_ref()); map.fold( @@ -670,10 +670,10 @@ mod tests { let mut rng = StdRng::seed_from_u64(seed); App::test((), |app| { - let buffer = app.add_model(|_| { + let buffer = app.add_model(|ctx| { let len = rng.gen_range(0..10); let text = RandomCharIter::new(&mut rng).take(len).collect::(); - Buffer::new(0, text) + Buffer::new(0, text, ctx) }); let mut map = FoldMap::new(buffer.clone(), app.as_ref()); @@ -749,7 +749,7 @@ mod tests { fn test_buffer_rows() { App::test((), |app| { let text = sample_text(6, 6) + "\n"; - let buffer = app.add_model(|_| Buffer::new(0, text)); + let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); let mut map = FoldMap::new(buffer.clone(), app.as_ref()); diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index e2c422ad36169ebedc44ef7367003ca1f1af3cdb..b69a70f3979870eb061f72c0cfdff25931165ccf 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -324,7 +324,7 @@ mod tests { fn test_chars_at() { App::test((), |app| { let text = sample_text(6, 6); - let buffer = app.add_model(|_| Buffer::new(0, text)); + let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); buffer .update(app, |buffer, ctx| { @@ -391,7 +391,7 @@ mod tests { #[test] fn test_max_point() { App::test((), |app| { - let buffer = app.add_model(|_| Buffer::new(0, "aaa\n\t\tbbb")); + let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx)); let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); assert_eq!( map.read(app).max_point(app.as_ref()), diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 24b6d8447b3be4593a9f2b688aeb60ffc51c4ec5..1d1cfaf0476908b05fd66de0bf325f674f141b40 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -8,8 +8,8 @@ use crate::{ watch::{self, Receiver}, worktree::FileHandle, }; -use gpui::{MutableAppContext, PathPromptOptions}; -use std::path::PathBuf; +use std::{collections::HashMap, fmt, path::PathBuf}; + pub fn init(app: &mut MutableAppContext) { app.add_global_action("workspace:open", open); app.add_global_action("workspace:open_paths", open_paths); @@ -31,12 +31,13 @@ use crate::{ use futures_core::{future::LocalBoxFuture, Future}; use gpui::{ color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, - ClipboardItem, Entity, EntityTask, ModelHandle, View, ViewContext, ViewHandle, + ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, PathPromptOptions, View, + ViewContext, ViewHandle, }; use log::error; use smol::prelude::*; use std::{ - collections::{hash_map::Entry, HashMap, HashSet}, + collections::{hash_map::Entry, HashSet}, path::Path, sync::Arc, }; @@ -98,6 +99,18 @@ fn quit(_: &(), app: &mut MutableAppContext) { app.platform().quit(); } +pub trait Item: Entity + Sized { + type View: ItemView; + + fn build_view( + handle: ModelHandle, + settings: watch::Receiver, + ctx: &mut ViewContext, + ) -> Self::View; + + fn file(&self) -> Option<&FileHandle>; +} + pub trait ItemView: View { fn title(&self, app: &AppContext) -> String; fn entry_id(&self, app: &AppContext) -> Option<(usize, Arc)>; @@ -114,7 +127,7 @@ pub trait ItemView: View { &mut self, _: Option, _: &mut ViewContext, - ) -> LocalBoxFuture<'static, anyhow::Result>; + ) -> LocalBoxFuture<'static, anyhow::Result<()>>; fn should_activate_item_on_event(_: &Self::Event) -> bool { false } @@ -123,6 +136,18 @@ pub trait ItemView: View { } } +pub trait ItemHandle: Send + Sync { + fn id(&self) -> usize; + fn file<'a>(&'a self, ctx: &'a AppContext) -> Option<&'a FileHandle>; + fn add_view( + &self, + window_id: usize, + settings: watch::Receiver, + app: &mut MutableAppContext, + ) -> Box; + fn boxed_clone(&self) -> Box; +} + pub trait ItemViewHandle: Send + Sync { fn title(&self, app: &AppContext) -> String; fn entry_id(&self, app: &AppContext) -> Option<(usize, Arc)>; @@ -136,7 +161,30 @@ pub trait ItemViewHandle: Send + Sync { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result>; + ) -> LocalBoxFuture<'static, anyhow::Result<()>>; +} + +impl ItemHandle for ModelHandle { + fn id(&self) -> usize { + self.id() + } + + fn file<'a>(&'a self, ctx: &'a AppContext) -> Option<&'a FileHandle> { + self.read(ctx).file() + } + + fn add_view( + &self, + window_id: usize, + settings: watch::Receiver, + app: &mut MutableAppContext, + ) -> Box { + Box::new(app.add_view(window_id, |ctx| T::build_view(self.clone(), settings, ctx))) + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } } impl ItemViewHandle for ViewHandle { @@ -179,7 +227,7 @@ impl ItemViewHandle for ViewHandle { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result> { + ) -> LocalBoxFuture<'static, anyhow::Result<()>> { self.update(ctx, |item, ctx| item.save(file, ctx)) } @@ -202,6 +250,18 @@ impl Clone for Box { } } +impl Clone for Box { + fn clone(&self) -> Box { + self.boxed_clone() + } +} + +impl fmt::Debug for Box { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ItemHandle {{id: {}}}", self.id()) + } +} + #[derive(Debug)] pub struct State { pub modal: Option, @@ -214,14 +274,13 @@ pub struct Workspace { center: PaneGroup, panes: Vec>, active_pane: ViewHandle, - loading_entries: HashSet<(usize, Arc)>, replica_id: ReplicaId, worktrees: HashSet>, - buffers: HashMap< - (usize, u64), - postage::watch::Receiver, Arc>>>, + items: Vec>, + loading_items: HashMap< + (usize, Arc), + postage::watch::Receiver, Arc>>>, >, - untitled_buffers: HashMap>, } impl Workspace { @@ -242,12 +301,11 @@ impl Workspace { center: PaneGroup::new(pane.id()), panes: vec![pane.clone()], active_pane: pane.clone(), - loading_entries: HashSet::new(), settings, replica_id, worktrees: Default::default(), - buffers: Default::default(), - untitled_buffers: Default::default(), + items: Default::default(), + loading_items: Default::default(), } } @@ -366,11 +424,10 @@ impl Workspace { } pub fn open_new_file(&mut self, _: &(), ctx: &mut ViewContext) { - let buffer = ctx.add_model(|_| Buffer::new(self.replica_id, "")); - let buffer_view = ctx.add_view(|ctx| { - BufferView::for_buffer(buffer.clone(), None, self.settings.clone(), ctx) - }); - self.untitled_buffers.insert(buffer_view.id(), buffer); + let buffer = ctx.add_model(|ctx| Buffer::new(self.replica_id, "", ctx)); + let buffer_view = + ctx.add_view(|ctx| BufferView::for_buffer(buffer.clone(), self.settings.clone(), ctx)); + self.items.push(Box::new(buffer)); self.add_item(Box::new(buffer_view), ctx); } @@ -380,10 +437,8 @@ impl Workspace { entry: (usize, Arc), ctx: &mut ViewContext, ) -> Option> { - if self.loading_entries.contains(&entry) { - return None; - } - + // If the active pane contains a view for this file, then activate + // that item view. if self .active_pane() .update(ctx, |pane, ctx| pane.activate_entry(entry.clone(), ctx)) @@ -391,6 +446,19 @@ impl Workspace { return None; } + let window_id = ctx.window_id(); + let settings = self.settings.clone(); + + // Otherwise, if this file is already open somewhere in the workspace, + // then add another view for it. + if let Some(item) = self.items.iter().find(|item| { + item.file(ctx.as_ref()) + .map_or(false, |f| f.entry_id() == entry) + }) { + self.add_item(item.add_view(window_id, settings, ctx.as_mut()), ctx); + return None; + } + let (worktree_id, path) = entry.clone(); let worktree = match self.worktrees.get(&worktree_id).cloned() { @@ -401,40 +469,31 @@ impl Workspace { } }; - let inode = match worktree.read(ctx).inode_for_path(&path) { - Some(inode) => inode, - None => { - log::error!("path {:?} does not exist", path); - return None; - } - }; - let file = worktree.file(path.clone(), ctx.as_ref()); if file.is_deleted() { log::error!("path {:?} does not exist", path); return None; } - self.loading_entries.insert(entry.clone()); - - if let Entry::Vacant(entry) = self.buffers.entry((worktree_id, inode)) { + if let Entry::Vacant(entry) = self.loading_items.entry(entry.clone()) { let (mut tx, rx) = postage::watch::channel(); entry.insert(rx); - let history = file.load_history(ctx.as_ref()); let replica_id = self.replica_id; - let buffer = ctx + let history = ctx .background_executor() - .spawn(async move { Ok(Buffer::from_history(replica_id, history.await?)) }); - ctx.spawn(buffer, move |_, from_history_result, ctx| { - *tx.borrow_mut() = Some(match from_history_result { - Ok(buffer) => Ok(ctx.add_model(|_| buffer)), + .spawn(file.load_history(ctx.as_ref())); + ctx.spawn(history, move |_, history, ctx| { + *tx.borrow_mut() = Some(match history { + Ok(history) => Ok(Box::new(ctx.add_model(|ctx| { + Buffer::from_history(replica_id, history, Some(file), ctx) + }))), Err(error) => Err(Arc::new(error)), }) }) .detach() } - let mut watch = self.buffers.get(&(worktree_id, inode)).unwrap().clone(); + let mut watch = self.loading_items.get(&entry).unwrap().clone(); Some(ctx.spawn( async move { loop { @@ -445,18 +504,12 @@ impl Workspace { } }, move |me, load_result, ctx| { - me.loading_entries.remove(&entry); + me.loading_items.remove(&entry); match load_result { - Ok(buffer_handle) => { - let buffer_view = Box::new(ctx.add_view(|ctx| { - BufferView::for_buffer( - buffer_handle, - Some(file), - me.settings.clone(), - ctx, - ) - })); - me.add_item(buffer_view, ctx); + Ok(item) => { + me.items.push(item.clone()); + let view = item.add_view(window_id, settings, ctx.as_mut()); + me.add_item(view, ctx); } Err(error) => { log::error!("error opening item: {}", error); @@ -484,20 +537,11 @@ impl Workspace { if let Some(path) = path { handle.update(ctx, move |this, ctx| { let file = this.file_for_path(&path, ctx); - let worktree_id = file.worktree_id(); let task = item.save(Some(file), ctx.as_mut()); - let item_id = item.id(); - ctx.spawn(task, move |this, result, _| match result { - Err(e) => { + ctx.spawn(task, move |_, result, _| { + if let Err(e) = result { error!("failed to save item: {:?}, ", e); } - Ok(inode) => { - if let Some(buffer) = this.untitled_buffers.remove(&item_id) { - let (_, rx) = - postage::watch::channel_with(Some(Ok(buffer))); - this.buffers.insert((worktree_id, inode), rx); - } - } }) .detach() }) @@ -760,15 +804,13 @@ mod tests { let file2 = entries[1].clone(); let file3 = entries[2].clone(); - let pane = app.read(|ctx| workspace.read(ctx).active_pane().clone()); - // Open the first entry workspace .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)) .unwrap() .await; app.read(|ctx| { - let pane = pane.read(ctx); + let pane = workspace.read(ctx).active_pane().read(ctx); assert_eq!( pane.active_item().unwrap().entry_id(ctx), Some(file1.clone()) @@ -782,7 +824,7 @@ mod tests { .unwrap() .await; app.read(|ctx| { - let pane = pane.read(ctx); + let pane = workspace.read(ctx).active_pane().read(ctx); assert_eq!( pane.active_item().unwrap().entry_id(ctx), Some(file2.clone()) @@ -795,7 +837,7 @@ mod tests { assert!(w.open_entry(file1.clone(), ctx).is_none()) }); app.read(|ctx| { - let pane = pane.read(ctx); + let pane = workspace.read(ctx).active_pane().read(ctx); assert_eq!( pane.active_item().unwrap().entry_id(ctx), Some(file1.clone()) @@ -803,21 +845,42 @@ mod tests { assert_eq!(pane.items().len(), 2); }); - // Open the third entry twice concurrently. Only one pane item is added. - workspace - .update(&mut app, |w, ctx| { - let task = w.open_entry(file3.clone(), ctx).unwrap(); - assert!(w.open_entry(file3.clone(), ctx).is_none()); - task - }) - .await; + // Split the pane with the first entry, then open the second entry again. + workspace.update(&mut app, |w, ctx| { + w.split_pane(w.active_pane().clone(), SplitDirection::Right, ctx); + assert!(w.open_entry(file2.clone(), ctx).is_none()); + assert_eq!( + w.active_pane() + .read(ctx) + .active_item() + .unwrap() + .entry_id(ctx.as_ref()), + Some(file2.clone()) + ); + }); + + // Open the third entry twice concurrently. Two pane items + // are added. + let (t1, t2) = workspace.update(&mut app, |w, ctx| { + ( + w.open_entry(file3.clone(), ctx).unwrap(), + w.open_entry(file3.clone(), ctx).unwrap(), + ) + }); + t1.await; + t2.await; app.read(|ctx| { - let pane = pane.read(ctx); + let pane = workspace.read(ctx).active_pane().read(ctx); assert_eq!( pane.active_item().unwrap().entry_id(ctx), Some(file3.clone()) ); - assert_eq!(pane.items().len(), 3); + let pane_entries = pane + .items() + .iter() + .map(|i| i.entry_id(ctx).unwrap()) + .collect::>(); + assert_eq!(pane_entries, &[file1, file2, file3.clone(), file3]); }); }); } @@ -1008,19 +1071,13 @@ mod tests { // Open the same newly-created file in another pane item. // The new editor should reuse the same buffer. - workspace - .update(&mut app, |workspace, ctx| { - workspace.open_new_file(&(), ctx); - workspace.split_pane( - workspace.active_pane().clone(), - SplitDirection::Right, - ctx, - ); - workspace - .open_entry((worktree.id(), Path::new("the-new-name").into()), ctx) - .unwrap() - }) - .await; + workspace.update(&mut app, |workspace, ctx| { + workspace.open_new_file(&(), ctx); + workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, ctx); + assert!(workspace + .open_entry((worktree.id(), Path::new("the-new-name").into()), ctx) + .is_none()); + }); let editor2 = workspace.update(&mut app, |workspace, ctx| { workspace .active_item(ctx) @@ -1030,7 +1087,7 @@ mod tests { .unwrap() }); app.read(|ctx| { - assert_eq!(editor.read(ctx).buffer(), editor2.read(ctx).buffer()); + assert_eq!(editor2.read(ctx).buffer(), editor.read(ctx).buffer()); }) }); } diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 00900cdb9d35b0ba6c6f8ca1939868d8e2eb0b51..ba6c37bac1bab31485bec9f98b874f41e8ec6ab8 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -9,7 +9,7 @@ use crate::{ use ::ignore::gitignore::Gitignore; use anyhow::{Context, Result}; pub use fuzzy::{match_paths, PathMatch}; -use gpui::{scoped_pool, AppContext, Entity, ModelContext, ModelHandle, Task, View, ViewContext}; +use gpui::{scoped_pool, AppContext, Entity, ModelContext, ModelHandle, Task}; use lazy_static::lazy_static; use parking_lot::Mutex; use postage::{ @@ -53,7 +53,7 @@ pub struct Worktree { poll_scheduled: bool, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FileHandle { worktree: ModelHandle, state: Arc>, @@ -191,18 +191,17 @@ impl Worktree { path: &Path, content: BufferSnapshot, ctx: &AppContext, - ) -> Task> { + ) -> Task> { let abs_path = self.snapshot.abs_path.join(path); ctx.background_executor().spawn(async move { let buffer_size = content.text_summary().bytes.min(10 * 1024); let file = std::fs::File::create(&abs_path)?; - let metadata = file.metadata()?; let mut writer = std::io::BufWriter::with_capacity(buffer_size, file); for chunk in content.fragments() { writer.write(chunk.as_bytes())?; } writer.flush()?; - Ok(metadata.ino()) + Ok(()) }) } } @@ -415,7 +414,7 @@ impl FileHandle { self.worktree.read(ctx).load_history(&self.path(), ctx) } - pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { + pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { let worktree = self.worktree.read(ctx); worktree.save(&self.path(), content, ctx) } @@ -428,14 +427,14 @@ impl FileHandle { (self.worktree.id(), self.path()) } - pub fn observe_from_view( + pub fn observe_from_model( &self, - ctx: &mut ViewContext, - mut callback: impl FnMut(&mut T, FileHandle, &mut ViewContext) + 'static, + ctx: &mut ModelContext, + mut callback: impl FnMut(&mut T, FileHandle, &mut ModelContext) + 'static, ) { let mut prev_state = self.state.lock().clone(); let cur_state = Arc::downgrade(&self.state); - ctx.observe_model(&self.worktree, move |observer, worktree, ctx| { + ctx.observe(&self.worktree, move |observer, worktree, ctx| { if let Some(cur_state) = cur_state.upgrade() { let cur_state_unlocked = cur_state.lock(); if *cur_state_unlocked != prev_state { @@ -1361,7 +1360,8 @@ mod tests { app.read(|ctx| tree.read(ctx).scan_complete()).await; app.read(|ctx| assert_eq!(tree.read(ctx).file_count(), 1)); - let buffer = app.add_model(|_| Buffer::new(1, "a line of text.\n".repeat(10 * 1024))); + let buffer = + app.add_model(|ctx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), ctx)); let path = tree.update(&mut app, |tree, ctx| { let path = tree.files(0).next().unwrap().path().clone(); From 7fb7a6adfc6d78d31055bc4a87a99682d5b716ac Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 09:32:14 +0200 Subject: [PATCH 06/27] Add a `Summary` trait in preparation of passing a context struct --- zed/src/editor/buffer/mod.rs | 23 ++++++++--------- zed/src/editor/buffer/text.rs | 10 ++++++-- zed/src/editor/display_map/fold_map.rs | 6 ++--- zed/src/operation_queue.rs | 11 +++----- zed/src/sum_tree/cursor.rs | 10 ++++++-- zed/src/sum_tree/mod.rs | 35 ++++++++++++++------------ zed/src/worktree.rs | 6 ++--- 7 files changed, 56 insertions(+), 45 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 363a6b926b733eb08c3251095fc449860463c9dd..ad7a06db6def02c6540c48d0b5c934b911815590 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -475,17 +475,17 @@ impl Buffer { if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); let summary_end = cmp::min(range.end - cursor.start(), fragment.len()); - summary += &fragment.text.slice(summary_start..summary_end).summary(); + summary += fragment.text.slice(summary_start..summary_end).summary(); cursor.next(); } if range.end > *cursor.start() { - summary += &cursor.summary::(&range.end, SeekBias::Right); + summary += cursor.summary::(&range.end, SeekBias::Right); if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); let summary_end = cmp::min(range.end - cursor.start(), fragment.len()); - summary += &fragment.text.slice(summary_start..summary_end).summary(); + summary += fragment.text.slice(summary_start..summary_end).summary(); } } @@ -520,17 +520,17 @@ impl Buffer { if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); let summary_end = cmp::min(range.end - cursor.start(), fragment.len()); - summary += &fragment.text.slice(summary_start..summary_end).summary(); + summary += fragment.text.slice(summary_start..summary_end).summary(); cursor.next(); } if range.end > *cursor.start() { - summary += &cursor.summary::(&range.end, SeekBias::Right); + summary += cursor.summary::(&range.end, SeekBias::Right); if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); let summary_end = cmp::min(range.end - cursor.start(), fragment.len()); - summary += &fragment.text.slice(summary_start..summary_end).summary(); + summary += fragment.text.slice(summary_start..summary_end).summary(); } } @@ -2102,8 +2102,8 @@ impl sum_tree::Item for Fragment { } } -impl<'a> AddAssign<&'a FragmentSummary> for FragmentSummary { - fn add_assign(&mut self, other: &Self) { +impl sum_tree::Summary for FragmentSummary { + fn add_summary(&mut self, other: &Self) { self.text_summary += &other.text_summary; debug_assert!(self.max_fragment_id <= other.max_fragment_id); self.max_fragment_id = other.max_fragment_id.clone(); @@ -2166,8 +2166,8 @@ impl sum_tree::Item for InsertionSplit { } } -impl<'a> AddAssign<&'a InsertionSplitSummary> for InsertionSplitSummary { - fn add_assign(&mut self, other: &Self) { +impl sum_tree::Summary for InsertionSplitSummary { + fn add_summary(&mut self, other: &Self) { self.extent += other.extent; } } @@ -3071,8 +3071,7 @@ mod tests { let mut buffers = Vec::new(); let mut network = Network::new(); for i in 0..PEERS { - let buffer = - ctx.add_model(|_| Buffer::new(i as ReplicaId, base_text.as_str())); + let buffer = 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); diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs index 0fff5c81d18af79877e33a9f3add8a7dcf301a61..56d66a3ef131bf3734d58a1e6a4f61478ec8c740 100644 --- a/zed/src/editor/buffer/text.rs +++ b/zed/src/editor/buffer/text.rs @@ -58,6 +58,12 @@ pub struct TextSummary { pub rightmost_point: Point, } +impl sum_tree::Summary for TextSummary { + fn add_summary(&mut self, other: &Self) { + *self += other; + } +} + impl<'a> std::ops::AddAssign<&'a Self> for TextSummary { fn add_assign(&mut self, other: &'a Self) { let joined_line_len = self.lines.column + other.first_line_len; @@ -85,8 +91,8 @@ impl std::ops::AddAssign for TextSummary { } impl<'a> sum_tree::Dimension<'a, TextSummary> for TextSummary { - fn add_summary(&mut self, summary: &TextSummary) { - *self += summary; + fn add_summary(&mut self, other: &TextSummary) { + *self += other; } } diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index d1bf169ef6673d23ca593f5aada72a2e1fdcf904..4b6a0bdb727ca6cf7a216e7e0cc17a7c6aa1e8ee 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -421,8 +421,8 @@ impl sum_tree::Item for Transform { } } -impl<'a> std::ops::AddAssign<&'a Self> for TransformSummary { - fn add_assign(&mut self, other: &'a Self) { +impl sum_tree::Summary for TransformSummary { + fn add_summary(&mut self, other: &Self) { self.buffer += &other.buffer; self.display += &other.display; } @@ -430,7 +430,7 @@ impl<'a> std::ops::AddAssign<&'a Self> for TransformSummary { impl<'a> Dimension<'a, TransformSummary> for TransformSummary { fn add_summary(&mut self, summary: &'a TransformSummary) { - *self += summary; + sum_tree::Summary::add_summary(self, summary); } } diff --git a/zed/src/operation_queue.rs b/zed/src/operation_queue.rs index 0603d5b9ff4976856a4dd3c8b424026a8427d95b..46af12bea2869bbe0e88ba55f31628491b85bd61 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/operation_queue.rs @@ -1,11 +1,8 @@ use crate::{ - sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree}, + sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree, Summary}, time, }; -use std::{ - fmt::Debug, - ops::{Add, AddAssign}, -}; +use std::{fmt::Debug, ops::Add}; pub trait Operation: Clone + Debug + Eq { fn timestamp(&self) -> time::Lamport; @@ -68,8 +65,8 @@ impl KeyedItem for T { } } -impl<'a> AddAssign<&'a Self> for OperationSummary { - fn add_assign(&mut self, other: &Self) { +impl Summary for OperationSummary { + fn add_summary(&mut self, other: &Self) { assert!(self.key < other.key); self.key = other.key; self.len += other.len; diff --git a/zed/src/sum_tree/cursor.rs b/zed/src/sum_tree/cursor.rs index 8b2e9e78b733cfecc40db1f3704df941c7df6dec..beaae1239a94872d67fb193028c8f650d3d665e1 100644 --- a/zed/src/sum_tree/cursor.rs +++ b/zed/src/sum_tree/cursor.rs @@ -511,7 +511,10 @@ where SeekAggregate::Slice(_) => { slice_items.push(item.clone()); slice_item_summaries.push(item_summary.clone()); - *slice_items_summary.as_mut().unwrap() += item_summary; + slice_items_summary + .as_mut() + .unwrap() + .add_summary(item_summary); } SeekAggregate::Summary(summary) => { summary.add_summary(item_summary); @@ -621,7 +624,10 @@ where SeekAggregate::None => {} SeekAggregate::Slice(_) => { slice_items.push(item.clone()); - *slice_items_summary.as_mut().unwrap() += item_summary; + slice_items_summary + .as_mut() + .unwrap() + .add_summary(item_summary); slice_item_summaries.push(item_summary.clone()); } SeekAggregate::Summary(summary) => { diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index fd104a3b1205fb2a7ad014ebae7861edc96e776f..da2077d31ecc6eb4e2b98fe4dd6d09810181d63a 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -3,7 +3,7 @@ mod cursor; use arrayvec::ArrayVec; pub use cursor::Cursor; pub use cursor::FilterCursor; -use std::{fmt, iter::FromIterator, ops::AddAssign, sync::Arc}; +use std::{fmt, iter::FromIterator, sync::Arc}; #[cfg(test)] const TREE_BASE: usize = 2; @@ -11,7 +11,7 @@ const TREE_BASE: usize = 2; const TREE_BASE: usize = 6; pub trait Item: Clone + fmt::Debug { - type Summary: for<'a> AddAssign<&'a Self::Summary> + Default + Clone + fmt::Debug; + type Summary: Summary; fn summary(&self) -> Self::Summary; } @@ -22,6 +22,10 @@ pub trait KeyedItem: Item { fn key(&self) -> Self::Key; } +pub trait Summary: Default + Clone + fmt::Debug { + fn add_summary(&mut self, summary: &Self); +} + pub trait Dimension<'a, Summary: Default>: Clone + fmt::Debug + Default { fn add_summary(&mut self, summary: &'a Summary); } @@ -136,7 +140,7 @@ impl SumTree { }) = leaf.as_mut() { let item_summary = item.summary(); - *summary += &item_summary; + summary.add_summary(&item_summary); items.push(item); item_summaries.push(item_summary); } else { @@ -183,7 +187,7 @@ impl SumTree { .. } => { let other_node = other.0.clone(); - *summary += other_node.summary(); + summary.add_summary(other_node.summary()); let height_delta = *height - other_node.height(); let mut summaries_to_append = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new(); @@ -277,7 +281,7 @@ impl SumTree { item_summaries: right_summaries, }))) } else { - *summary += other_node.summary(); + summary.add_summary(other_node.summary()); items.extend(other_node.items().iter().cloned()); item_summaries.extend(other_node.child_summaries().iter().cloned()); None @@ -484,12 +488,12 @@ impl Edit { fn sum<'a, T, I>(iter: I) -> T where - T: 'a + Default + AddAssign<&'a T>, + T: 'a + Summary, I: Iterator, { let mut sum = T::default(); for value in iter { - sum += value; + sum.add_summary(value); } sum } @@ -836,15 +840,8 @@ mod tests { *self } } - - impl<'a> Dimension<'a, IntegersSummary> for u8 { - fn add_summary(&mut self, summary: &IntegersSummary) { - *self = summary.max; - } - } - - impl<'a> AddAssign<&'a Self> for IntegersSummary { - fn add_assign(&mut self, other: &Self) { + impl Summary for IntegersSummary { + fn add_summary(&mut self, other: &Self) { self.count.0 += &other.count.0; self.sum.0 += &other.sum.0; self.contains_even |= other.contains_even; @@ -852,6 +849,12 @@ mod tests { } } + impl<'a> Dimension<'a, IntegersSummary> for u8 { + fn add_summary(&mut self, summary: &IntegersSummary) { + *self = summary.max; + } + } + impl<'a> Dimension<'a, IntegersSummary> for Count { fn add_summary(&mut self, summary: &IntegersSummary) { self.0 += summary.count.0; diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index ea023fb813d49f2260abbea835aa97b07a33470e..615dbc3b05bb9577bb14e2f029eabd6ac186c5b6 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -24,7 +24,7 @@ use std::{ fmt, fs, future::Future, io::{self, Read, Write}, - ops::{AddAssign, Deref}, + ops::Deref, os::unix::{ffi::OsStrExt, fs::MetadataExt}, path::{Path, PathBuf}, sync::{Arc, Weak}, @@ -535,8 +535,8 @@ impl Default for EntrySummary { } } -impl<'a> AddAssign<&'a EntrySummary> for EntrySummary { - fn add_assign(&mut self, rhs: &'a EntrySummary) { +impl sum_tree::Summary for EntrySummary { + fn add_summary(&mut self, rhs: &Self) { self.max_path = rhs.max_path.clone(); self.file_count += rhs.file_count; self.visible_file_count += rhs.visible_file_count; From 901525bf77ccb32d26d85184fce639c668d2801b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 16:13:35 +0200 Subject: [PATCH 07/27] Implement `FoldMap`'s folds using a `SumTree` This required passing a `Context` object to `Summary` and introducing a new `SeekDimension` trait that allows comparing two dimensions and pass an additional context object. --- zed/src/editor/buffer/mod.rs | 4 + zed/src/editor/buffer/text.rs | 2 + zed/src/editor/display_map/fold_map.rs | 229 +++++++++++++++++++------ zed/src/editor/display_map/mod.rs | 2 +- zed/src/operation_queue.rs | 2 + zed/src/sum_tree/cursor.rs | 121 +++++++++---- zed/src/sum_tree/mod.rs | 104 ++++++++--- zed/src/util.rs | 39 ----- zed/src/worktree.rs | 2 + 9 files changed, 354 insertions(+), 151 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index ad7a06db6def02c6540c48d0b5c934b911815590..9a35a61de3674a3016856a2a2e43358d11e781dc 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2103,6 +2103,8 @@ impl sum_tree::Item for Fragment { } impl sum_tree::Summary for FragmentSummary { + type Context = (); + fn add_summary(&mut self, other: &Self) { self.text_summary += &other.text_summary; debug_assert!(self.max_fragment_id <= other.max_fragment_id); @@ -2167,6 +2169,8 @@ impl sum_tree::Item for InsertionSplit { } impl sum_tree::Summary for InsertionSplitSummary { + type Context = (); + fn add_summary(&mut self, other: &Self) { self.extent += other.extent; } diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs index 56d66a3ef131bf3734d58a1e6a4f61478ec8c740..02f584a3952bedb10f0d1931f2628cc29738db8b 100644 --- a/zed/src/editor/buffer/text.rs +++ b/zed/src/editor/buffer/text.rs @@ -59,6 +59,8 @@ pub struct TextSummary { } impl sum_tree::Summary for TextSummary { + type Context = (); + fn add_summary(&mut self, other: &Self) { *self += other; } diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 4b6a0bdb727ca6cf7a216e7e0cc17a7c6aa1e8ee..21d033ce64e7aca27e22575334f92fba093603c0 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -1,10 +1,10 @@ use super::{ - buffer, Anchor, AnchorRangeExt, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset, + buffer::{self, AnchorRangeExt}, + Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset, }; use crate::{ - sum_tree::{self, Cursor, SumTree}, + sum_tree::{self, Cursor, SeekBias, SumTree}, time, - util::find_insertion_index, }; use anyhow::{anyhow, Result}; use gpui::{AppContext, ModelHandle}; @@ -14,12 +14,11 @@ use std::{ iter::Take, ops::Range, }; -use sum_tree::{Dimension, SeekBias}; pub struct FoldMap { buffer: ModelHandle, transforms: Mutex>, - folds: Vec>, + folds: SumTree, last_sync: Mutex, } @@ -29,7 +28,7 @@ impl FoldMap { let text_summary = buffer.text_summary(); Self { buffer: buffer_handle, - folds: Vec::new(), + folds: Default::default(), transforms: Mutex::new(SumTree::from_item(Transform { summary: TransformSummary { buffer: text_summary.clone(), @@ -76,17 +75,20 @@ impl FoldMap { pub fn folds_in_range<'a, T>( &'a self, range: Range, - app: &'a AppContext, + ctx: &'a AppContext, ) -> Result>> where T: ToOffset, { - let buffer = self.buffer.read(app); + let buffer = self.buffer.read(ctx); let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?; - Ok(self.folds.iter().filter(move |fold| { - range.start.cmp(&fold.end, buffer).unwrap() == Ordering::Less - && range.end.cmp(&fold.start, buffer).unwrap() == Ordering::Greater - })) + Ok(self + .folds + .filter::<_, usize>(move |summary| { + range.start.cmp(&summary.max_end, buffer).unwrap() < Ordering::Equal + && range.end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal + }) + .map(|f| &f.0)) } pub fn fold( @@ -99,16 +101,31 @@ impl FoldMap { let mut edits = Vec::new(); let buffer = self.buffer.read(ctx); for range in ranges.into_iter() { - let start = range.start.to_offset(buffer)?; - let end = range.end.to_offset(buffer)?; + let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?; + let fold = if range.start == range.end { + Fold(buffer.anchor_after(range.start)?..buffer.anchor_after(range.end)?) + } else { + Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?) + }; edits.push(Edit { - old_range: start..end, - new_range: start..end, + old_range: range.clone(), + new_range: range.clone(), }); - - let fold = buffer.anchor_after(start)?..buffer.anchor_before(end)?; - let ix = find_insertion_index(&self.folds, |probe| probe.cmp(&fold, buffer))?; - self.folds.insert(ix, fold); + self.folds = { + let mut new_tree = SumTree::new(); + let mut cursor = self.folds.cursor::<_, ()>(); + new_tree.push_tree_with_ctx( + cursor.slice_with_ctx( + &FoldRange(fold.0.clone()), + SeekBias::Right, + Some(buffer), + ), + Some(buffer), + ); + new_tree.push_with_ctx(fold, Some(buffer)); + new_tree.push_tree_with_ctx(cursor.suffix_with_ctx(Some(buffer)), Some(buffer)); + new_tree + }; } edits.sort_unstable_by(|a, b| { a.old_range @@ -131,27 +148,45 @@ impl FoldMap { let buffer = self.buffer.read(ctx); let mut edits = Vec::new(); + let mut fold_ixs_to_delete = Vec::new(); for range in ranges.into_iter() { let start = buffer.anchor_before(range.start.to_offset(buffer)?)?; let end = buffer.anchor_after(range.end.to_offset(buffer)?)?; + let range = start..end; // Remove intersecting folds and add their ranges to edits that are passed to apply_edits - self.folds.retain(|fold| { - if fold.start.cmp(&end, buffer).unwrap() > Ordering::Equal - || fold.end.cmp(&start, buffer).unwrap() < Ordering::Equal - { - true - } else { - let offset_range = - fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap(); - edits.push(Edit { - old_range: offset_range.clone(), - new_range: offset_range, - }); - false - } + let mut cursor = self.folds.filter::<_, usize>(|summary| { + range.start.cmp(&summary.max_end, buffer).unwrap() < Ordering::Equal + && range.end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal }); + + while let Some(fold) = cursor.item() { + let offset_range = + fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap(); + edits.push(Edit { + old_range: offset_range.clone(), + new_range: offset_range, + }); + fold_ixs_to_delete.push(*cursor.start()); + cursor.next(); + } } + fold_ixs_to_delete.sort_unstable(); + fold_ixs_to_delete.dedup(); + + self.folds = { + let mut cursor = self.folds.cursor::<_, ()>(); + let mut folds = SumTree::new(); + for fold_ix in fold_ixs_to_delete { + folds.push_tree_with_ctx( + cursor.slice_with_ctx(&fold_ix, SeekBias::Right, Some(buffer)), + Some(buffer), + ); + cursor.next(); + } + folds.push_tree_with_ctx(cursor.suffix_with_ctx(Some(buffer)), Some(buffer)); + folds + }; self.apply_edits(edits, ctx); Ok(()) @@ -262,14 +297,14 @@ impl FoldMap { ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize; let anchor = buffer.anchor_before(edit.new_range.start).unwrap(); - let folds_start = - find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer)) - .unwrap(); - let mut folds = self.folds[folds_start..] - .iter() - .map(|fold| { - fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap() - }) + let mut folds_cursor = self.folds.cursor::<_, ()>(); + folds_cursor.seek_with_ctx( + &FoldRange(anchor..Anchor::End), + SeekBias::Left, + Some(buffer), + ); + let mut folds = folds_cursor + .map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap()) .peekable(); while folds @@ -422,18 +457,112 @@ impl sum_tree::Item for Transform { } impl sum_tree::Summary for TransformSummary { + type Context = (); + fn add_summary(&mut self, other: &Self) { self.buffer += &other.buffer; self.display += &other.display; } } -impl<'a> Dimension<'a, TransformSummary> for TransformSummary { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary { fn add_summary(&mut self, summary: &'a TransformSummary) { sum_tree::Summary::add_summary(self, summary); } } +#[derive(Clone, Debug)] +struct Fold(Range); + +impl sum_tree::Item for Fold { + type Summary = FoldSummary; + + fn summary(&self) -> Self::Summary { + FoldSummary { + start: self.0.start.clone(), + end: self.0.end.clone(), + min_start: self.0.start.clone(), + max_end: self.0.end.clone(), + count: 1, + } + } +} + +#[derive(Clone, Debug)] +struct FoldSummary { + start: Anchor, + end: Anchor, + min_start: Anchor, + max_end: Anchor, + count: usize, +} + +impl Default for FoldSummary { + fn default() -> Self { + Self { + start: Anchor::Start, + end: Anchor::End, + min_start: Anchor::Start, + max_end: Anchor::Start, + count: 0, + } + } +} + +impl sum_tree::Summary for FoldSummary { + type Context = Buffer; + + fn add_summary_with_ctx(&mut self, other: &Self, buffer: Option<&Self::Context>) { + let buffer = buffer.unwrap(); + if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less { + self.min_start = other.min_start.clone(); + } + if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater { + self.max_end = other.max_end.clone(); + } + + if cfg!(debug_assertions) { + let start_comparison = self.start.cmp(&other.start, buffer).unwrap(); + let end_comparison = self.end.cmp(&other.end, buffer).unwrap(); + assert!(start_comparison <= Ordering::Equal); + if start_comparison == Ordering::Equal { + assert!(end_comparison >= Ordering::Equal); + } + } + self.start = other.start.clone(); + self.end = other.end.clone(); + self.count += other.count; + } +} + +#[derive(Clone, Debug)] +struct FoldRange(Range); + +impl Default for FoldRange { + fn default() -> Self { + Self(Anchor::Start..Anchor::End) + } +} + +impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange { + fn add_summary(&mut self, summary: &'a FoldSummary) { + self.0.start = summary.start.clone(); + self.0.end = summary.end.clone(); + } +} + +impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for FoldRange { + fn cmp(&self, other: &Self, buffer: Option<&Buffer>) -> Ordering { + self.0.cmp(&other.0, buffer.unwrap()).unwrap() + } +} + +impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize { + fn add_summary(&mut self, summary: &'a FoldSummary) { + *self += summary.count; + } +} + pub struct BufferRows<'a> { cursor: Cursor<'a, Transform, DisplayPoint, TransformSummary>, display_point: Point, @@ -498,7 +627,7 @@ impl<'a> Iterator for Chars<'a> { } } -impl<'a> Dimension<'a, TransformSummary> for DisplayPoint { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint { fn add_summary(&mut self, summary: &'a TransformSummary) { self.0 += &summary.display.lines; } @@ -507,19 +636,19 @@ impl<'a> Dimension<'a, TransformSummary> for DisplayPoint { #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct DisplayOffset(usize); -impl<'a> Dimension<'a, TransformSummary> for DisplayOffset { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset { fn add_summary(&mut self, summary: &'a TransformSummary) { self.0 += &summary.display.chars; } } -impl<'a> Dimension<'a, TransformSummary> for Point { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point { fn add_summary(&mut self, summary: &'a TransformSummary) { *self += &summary.buffer.lines; } } -impl<'a> Dimension<'a, TransformSummary> for usize { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize { fn add_summary(&mut self, summary: &'a TransformSummary) { *self += &summary.buffer.chars; } @@ -894,13 +1023,13 @@ mod tests { fn merged_fold_ranges(&self, app: &AppContext) -> Vec> { let buffer = self.buffer.read(app); - let mut folds = self.folds.clone(); + let mut folds = self.folds.items(); // Ensure sorting doesn't change how folds get merged and displayed. - folds.sort_by(|a, b| a.cmp(b, buffer).unwrap()); + folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap()); let mut fold_ranges = folds .iter() .map(|fold| { - fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap() + fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap() }) .peekable(); diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 92ccd21acbda2fc62c0e9423dea42dde33686df6..aaafd771f9d63cee8027a09821967dda8a92dfcd 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -1,6 +1,6 @@ mod fold_map; -use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint}; +use super::{buffer, Anchor, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint}; use anyhow::Result; pub use fold_map::BufferRows; use fold_map::{FoldMap, FoldMapSnapshot}; diff --git a/zed/src/operation_queue.rs b/zed/src/operation_queue.rs index 46af12bea2869bbe0e88ba55f31628491b85bd61..27b0550b7638b5e7eceb9aa1a23876961b2812d6 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/operation_queue.rs @@ -66,6 +66,8 @@ impl KeyedItem for T { } impl Summary for OperationSummary { + type Context = (); + fn add_summary(&mut self, other: &Self) { assert!(self.key < other.key); self.key = other.key; diff --git a/zed/src/sum_tree/cursor.rs b/zed/src/sum_tree/cursor.rs index beaae1239a94872d67fb193028c8f650d3d665e1..44647ceb37e6c00ba2f59b0998b4629cb4622399 100644 --- a/zed/src/sum_tree/cursor.rs +++ b/zed/src/sum_tree/cursor.rs @@ -382,22 +382,48 @@ where impl<'a, T, S, U> Cursor<'a, T, S, U> where T: Item, - S: Dimension<'a, T::Summary> + Ord, + S: SeekDimension<'a, T::Summary>, U: Dimension<'a, T::Summary>, { pub fn seek(&mut self, pos: &S, bias: SeekBias) -> bool { + self.seek_with_ctx(pos, bias, None) + } + + pub fn seek_with_ctx( + &mut self, + pos: &S, + bias: SeekBias, + ctx: Option<&'a ::Context>, + ) -> bool { self.reset(); - self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None) + self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, ctx) } - #[allow(unused)] pub fn seek_forward(&mut self, pos: &S, bias: SeekBias) -> bool { - self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None) + self.seek_forward_with_ctx(pos, bias, None) + } + + pub fn seek_forward_with_ctx( + &mut self, + pos: &S, + bias: SeekBias, + ctx: Option<&'a ::Context>, + ) -> bool { + self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, ctx) } pub fn slice(&mut self, end: &S, bias: SeekBias) -> SumTree { + self.slice_with_ctx(end, bias, None) + } + + pub fn slice_with_ctx( + &mut self, + end: &S, + bias: SeekBias, + ctx: Option<&'a ::Context>, + ) -> SumTree { let mut slice = SeekAggregate::Slice(SumTree::new()); - self.seek_internal::<()>(end, bias, &mut slice); + self.seek_internal::<()>(end, bias, &mut slice, ctx); if let SeekAggregate::Slice(slice) = slice { slice } else { @@ -406,9 +432,16 @@ where } pub fn suffix(&mut self) -> SumTree { + self.suffix_with_ctx(None) + } + + pub fn suffix_with_ctx( + &mut self, + ctx: Option<&'a ::Context>, + ) -> SumTree { let extent = self.tree.extent::(); let mut slice = SeekAggregate::Slice(SumTree::new()); - self.seek_internal::<()>(&extent, SeekBias::Right, &mut slice); + self.seek_internal::<()>(&extent, SeekBias::Right, &mut slice, ctx); if let SeekAggregate::Slice(slice) = slice { slice } else { @@ -417,11 +450,23 @@ where } pub fn summary(&mut self, end: &S, bias: SeekBias) -> D + where + D: Dimension<'a, T::Summary>, + { + self.summary_with_ctx(end, bias, None) + } + + pub fn summary_with_ctx( + &mut self, + end: &S, + bias: SeekBias, + ctx: Option<&'a ::Context>, + ) -> D where D: Dimension<'a, T::Summary>, { let mut summary = SeekAggregate::Summary(D::default()); - self.seek_internal(end, bias, &mut summary); + self.seek_internal(end, bias, &mut summary, ctx); if let SeekAggregate::Summary(summary) = summary { summary } else { @@ -434,11 +479,12 @@ where target: &S, bias: SeekBias, aggregate: &mut SeekAggregate, + ctx: Option<&'a ::Context>, ) -> bool where D: Dimension<'a, T::Summary>, { - debug_assert!(target >= &self.seek_dimension); + debug_assert!(target.cmp(&self.seek_dimension, ctx) >= Ordering::Equal); let mut containing_subtree = None; if self.did_seek { @@ -458,7 +504,7 @@ where let mut child_end = self.seek_dimension.clone(); child_end.add_summary(&child_summary); - let comparison = target.cmp(&child_end); + let comparison = target.cmp(&child_end, ctx); if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == SeekBias::Right) { @@ -467,7 +513,7 @@ where match aggregate { SeekAggregate::None => {} SeekAggregate::Slice(slice) => { - slice.push_tree(child_tree.clone()); + slice.push_tree_with_ctx(child_tree.clone(), ctx); } SeekAggregate::Summary(summary) => { summary.add_summary(child_summary); @@ -500,7 +546,7 @@ where let mut item_end = self.seek_dimension.clone(); item_end.add_summary(item_summary); - let comparison = target.cmp(&item_end); + let comparison = target.cmp(&item_end, ctx); if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == SeekBias::Right) { @@ -514,7 +560,7 @@ where slice_items_summary .as_mut() .unwrap() - .add_summary(item_summary); + .add_summary_with_ctx(item_summary, ctx); } SeekAggregate::Summary(summary) => { summary.add_summary(item_summary); @@ -523,11 +569,14 @@ where entry.index += 1; } else { if let SeekAggregate::Slice(slice) = aggregate { - slice.push_tree(SumTree(Arc::new(Node::Leaf { - summary: slice_items_summary.unwrap(), - items: slice_items, - item_summaries: slice_item_summaries, - }))); + slice.push_tree_with_ctx( + SumTree(Arc::new(Node::Leaf { + summary: slice_items_summary.unwrap(), + items: slice_items, + item_summaries: slice_item_summaries, + })), + ctx, + ); } break 'outer; } @@ -535,11 +584,14 @@ where if let SeekAggregate::Slice(slice) = aggregate { if !slice_items.is_empty() { - slice.push_tree(SumTree(Arc::new(Node::Leaf { - summary: slice_items_summary.unwrap(), - items: slice_items, - item_summaries: slice_item_summaries, - }))); + slice.push_tree_with_ctx( + SumTree(Arc::new(Node::Leaf { + summary: slice_items_summary.unwrap(), + items: slice_items, + item_summaries: slice_item_summaries, + })), + ctx, + ); } } } @@ -568,7 +620,7 @@ where let mut child_end = self.seek_dimension.clone(); child_end.add_summary(child_summary); - let comparison = target.cmp(&child_end); + let comparison = target.cmp(&child_end, ctx); if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == SeekBias::Right) { @@ -577,7 +629,7 @@ where match aggregate { SeekAggregate::None => {} SeekAggregate::Slice(slice) => { - slice.push_tree(child_trees[index].clone()); + slice.push_tree_with_ctx(child_trees[index].clone(), ctx); } SeekAggregate::Summary(summary) => { summary.add_summary(child_summary); @@ -614,7 +666,7 @@ where let mut child_end = self.seek_dimension.clone(); child_end.add_summary(item_summary); - let comparison = target.cmp(&child_end); + let comparison = target.cmp(&child_end, ctx); if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == SeekBias::Right) { @@ -627,7 +679,7 @@ where slice_items_summary .as_mut() .unwrap() - .add_summary(item_summary); + .add_summary_with_ctx(item_summary, ctx); slice_item_summaries.push(item_summary.clone()); } SeekAggregate::Summary(summary) => { @@ -647,11 +699,14 @@ where if let SeekAggregate::Slice(slice) = aggregate { if !slice_items.is_empty() { - slice.push_tree(SumTree(Arc::new(Node::Leaf { - summary: slice_items_summary.unwrap(), - items: slice_items, - item_summaries: slice_item_summaries, - }))); + slice.push_tree_with_ctx( + SumTree(Arc::new(Node::Leaf { + summary: slice_items_summary.unwrap(), + items: slice_items, + item_summaries: slice_item_summaries, + })), + ctx, + ); } } } @@ -672,9 +727,9 @@ where if let Some(summary) = self.item_summary() { end.add_summary(summary); } - *target == end + target.cmp(&end, ctx) == Ordering::Equal } else { - *target == self.seek_dimension + target.cmp(&self.seek_dimension, ctx) == Ordering::Equal } } } diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index da2077d31ecc6eb4e2b98fe4dd6d09810181d63a..0c85eaa8ef8b7146c64e39285ea789fe921ba173 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -3,7 +3,7 @@ mod cursor; use arrayvec::ArrayVec; pub use cursor::Cursor; pub use cursor::FilterCursor; -use std::{fmt, iter::FromIterator, sync::Arc}; +use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc}; #[cfg(test)] const TREE_BASE: usize = 2; @@ -23,17 +23,36 @@ pub trait KeyedItem: Item { } pub trait Summary: Default + Clone + fmt::Debug { - fn add_summary(&mut self, summary: &Self); + type Context; + + fn add_summary(&mut self, _summary: &Self) { + unimplemented!(); + } + + fn add_summary_with_ctx(&mut self, summary: &Self, ctx: Option<&Self::Context>) { + assert!(ctx.is_none()); + self.add_summary(summary); + } } -pub trait Dimension<'a, Summary: Default>: Clone + fmt::Debug + Default { - fn add_summary(&mut self, summary: &'a Summary); +pub trait Dimension<'a, S: Summary>: Clone + fmt::Debug + Default { + fn add_summary(&mut self, _summary: &'a S); } -impl<'a, T: Default> Dimension<'a, T> for () { +impl<'a, T: Summary> Dimension<'a, T> for () { fn add_summary(&mut self, _: &'a T) {} } +pub trait SeekDimension<'a, T: Summary>: Dimension<'a, T> { + fn cmp(&self, other: &Self, ctx: Option<&T::Context>) -> Ordering; +} + +impl<'a, S: Summary, T: Dimension<'a, S> + Ord> SeekDimension<'a, S> for T { + fn cmp(&self, other: &Self, _ctx: Option<&S::Context>) -> Ordering { + Ord::cmp(self, other) + } +} + #[derive(Copy, Clone, Eq, PartialEq)] pub enum SeekBias { Left, @@ -94,7 +113,7 @@ impl SumTree { let mut extent = D::default(); match self.0.as_ref() { Node::Internal { summary, .. } | Node::Leaf { summary, .. } => { - extent.add_summary(summary) + extent.add_summary(summary); } } extent @@ -154,30 +173,50 @@ impl SumTree { } pub fn push(&mut self, item: T) { + self.push_with_ctx(item, None); + } + + pub fn push_with_ctx(&mut self, item: T, ctx: Option<&::Context>) { let summary = item.summary(); - self.push_tree(SumTree::from_child_trees(vec![SumTree(Arc::new( - Node::Leaf { - summary: summary.clone(), - items: ArrayVec::from_iter(Some(item)), - item_summaries: ArrayVec::from_iter(Some(summary)), - }, - ))])) + self.push_tree_with_ctx( + SumTree::from_child_trees( + vec![SumTree(Arc::new(Node::Leaf { + summary: summary.clone(), + items: ArrayVec::from_iter(Some(item)), + item_summaries: ArrayVec::from_iter(Some(summary)), + }))], + ctx, + ), + ctx, + ) } pub fn push_tree(&mut self, other: Self) { + self.push_tree_with_ctx(other, None); + } + + pub fn push_tree_with_ctx( + &mut self, + other: Self, + ctx: Option<&::Context>, + ) { let other_node = other.0.clone(); if !other_node.is_leaf() || other_node.items().len() > 0 { if self.0.height() < other_node.height() { for tree in other_node.child_trees() { - self.push_tree(tree.clone()); + self.push_tree_with_ctx(tree.clone(), ctx); } - } else if let Some(split_tree) = self.push_tree_recursive(other) { - *self = Self::from_child_trees(vec![self.clone(), split_tree]); + } else if let Some(split_tree) = self.push_tree_recursive(other, ctx) { + *self = Self::from_child_trees(vec![self.clone(), split_tree], ctx); } } } - fn push_tree_recursive(&mut self, other: SumTree) -> Option> { + fn push_tree_recursive( + &mut self, + other: SumTree, + ctx: Option<&::Context>, + ) -> Option> { match Arc::make_mut(&mut self.0) { Node::Internal { height, @@ -187,7 +226,7 @@ impl SumTree { .. } => { let other_node = other.0.clone(); - summary.add_summary(other_node.summary()); + summary.add_summary_with_ctx(other_node.summary(), ctx); let height_delta = *height - other_node.height(); let mut summaries_to_append = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new(); @@ -199,7 +238,10 @@ impl SumTree { summaries_to_append.push(other_node.summary().clone()); trees_to_append.push(other) } else { - let tree_to_append = child_trees.last_mut().unwrap().push_tree_recursive(other); + let tree_to_append = child_trees + .last_mut() + .unwrap() + .push_tree_recursive(other, ctx); *child_summaries.last_mut().unwrap() = child_trees.last().unwrap().0.summary().clone(); @@ -229,13 +271,13 @@ impl SumTree { left_trees = all_trees.by_ref().take(midpoint).collect(); right_trees = all_trees.collect(); } - *summary = sum(left_summaries.iter()); + *summary = sum(left_summaries.iter(), ctx); *child_summaries = left_summaries; *child_trees = left_trees; Some(SumTree(Arc::new(Node::Internal { height: *height, - summary: sum(right_summaries.iter()), + summary: sum(right_summaries.iter(), ctx), child_summaries: right_summaries, child_trees: right_trees, }))) @@ -274,14 +316,14 @@ impl SumTree { } *items = left_items; *item_summaries = left_summaries; - *summary = sum(item_summaries.iter()); + *summary = sum(item_summaries.iter(), ctx); Some(SumTree(Arc::new(Node::Leaf { items: right_items, - summary: sum(right_summaries.iter()), + summary: sum(right_summaries.iter(), ctx), item_summaries: right_summaries, }))) } else { - summary.add_summary(other_node.summary()); + summary.add_summary_with_ctx(other_node.summary(), ctx); items.extend(other_node.items().iter().cloned()); item_summaries.extend(other_node.child_summaries().iter().cloned()); None @@ -290,13 +332,16 @@ impl SumTree { } } - fn from_child_trees(child_trees: Vec>) -> Self { + fn from_child_trees( + child_trees: Vec>, + ctx: Option<&::Context>, + ) -> Self { let height = child_trees[0].0.height() + 1; let mut child_summaries = ArrayVec::new(); for child in &child_trees { child_summaries.push(child.0.summary().clone()); } - let summary = sum(child_summaries.iter()); + let summary = sum(child_summaries.iter(), ctx); SumTree(Arc::new(Node::Internal { height, summary, @@ -486,14 +531,14 @@ impl Edit { } } -fn sum<'a, T, I>(iter: I) -> T +fn sum<'a, T, I>(iter: I, ctx: Option<&T::Context>) -> T where T: 'a + Summary, I: Iterator, { let mut sum = T::default(); for value in iter { - sum.add_summary(value); + sum.add_summary_with_ctx(value, ctx); } sum } @@ -840,7 +885,10 @@ mod tests { *self } } + impl Summary for IntegersSummary { + type Context = (); + fn add_summary(&mut self, other: &Self) { self.count.0 += &other.count.0; self.sum.0 += &other.sum.0; diff --git a/zed/src/util.rs b/zed/src/util.rs index 09ce2f87e155c30c996f75f5b1a8a62127ff9c4f..1ce914ddd46c272af2deb69c85ec7c0a8e5eca28 100644 --- a/zed/src/util.rs +++ b/zed/src/util.rs @@ -30,37 +30,6 @@ where } } -pub fn find_insertion_index<'a, F, T, E>(slice: &'a [T], mut f: F) -> Result -where - F: FnMut(&'a T) -> Result, -{ - use Ordering::*; - - let s = slice; - let mut size = s.len(); - if size == 0 { - return Ok(0); - } - let mut base = 0usize; - while size > 1 { - let half = size / 2; - let mid = base + half; - // mid is always in [0, size), that means mid is >= 0 and < size. - // mid >= 0: by definition - // mid < size: mid = size / 2 + size / 4 + size / 8 ... - let cmp = f(unsafe { s.get_unchecked(mid) })?; - base = if cmp == Greater { base } else { mid }; - size -= half; - } - // base is always in [0, size) because base <= mid. - let cmp = f(unsafe { s.get_unchecked(base) })?; - if cmp == Equal { - Ok(base) - } else { - Ok(base + (cmp == Less) as usize) - } -} - pub struct RandomCharIter(T); impl RandomCharIter { @@ -85,14 +54,6 @@ impl Iterator for RandomCharIter { mod tests { use super::*; - #[test] - fn test_find_insertion_index() { - assert_eq!( - find_insertion_index(&[0, 4, 8], |probe| Ok::(probe.cmp(&2))), - Ok(1) - ); - } - #[test] fn test_extend_sorted() { let mut vec = vec![]; diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 615dbc3b05bb9577bb14e2f029eabd6ac186c5b6..eb5172d57736647b5775bbe7d2249ebee13ba68a 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -536,6 +536,8 @@ impl Default for EntrySummary { } impl sum_tree::Summary for EntrySummary { + type Context = (); + fn add_summary(&mut self, rhs: &Self) { self.max_path = rhs.max_path.clone(); self.file_count += rhs.file_count; From 652fc9e4ec2fc6c27e1cf4de3f3ece0e33f4b610 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 16:57:47 +0200 Subject: [PATCH 08/27] Remove `add_summary_with_ctx` and always require a ctx in `add_summary` --- zed/src/editor/buffer/mod.rs | 4 ++-- zed/src/editor/buffer/text.rs | 2 +- zed/src/editor/display_map/fold_map.rs | 6 +++--- zed/src/operation_queue.rs | 2 +- zed/src/sum_tree/cursor.rs | 4 ++-- zed/src/sum_tree/mod.rs | 26 +++++++++++++------------- zed/src/worktree.rs | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 9a35a61de3674a3016856a2a2e43358d11e781dc..bb796ace93ca0a6138369360a4825a06a96d7cb2 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2105,7 +2105,7 @@ impl sum_tree::Item for Fragment { impl sum_tree::Summary for FragmentSummary { type Context = (); - fn add_summary(&mut self, other: &Self) { + fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { self.text_summary += &other.text_summary; debug_assert!(self.max_fragment_id <= other.max_fragment_id); self.max_fragment_id = other.max_fragment_id.clone(); @@ -2171,7 +2171,7 @@ impl sum_tree::Item for InsertionSplit { impl sum_tree::Summary for InsertionSplitSummary { type Context = (); - fn add_summary(&mut self, other: &Self) { + fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { self.extent += other.extent; } } diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs index 02f584a3952bedb10f0d1931f2628cc29738db8b..140fe2257e324f462e2d9047dc9fc180479e7af9 100644 --- a/zed/src/editor/buffer/text.rs +++ b/zed/src/editor/buffer/text.rs @@ -61,7 +61,7 @@ pub struct TextSummary { impl sum_tree::Summary for TextSummary { type Context = (); - fn add_summary(&mut self, other: &Self) { + fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { *self += other; } } diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 21d033ce64e7aca27e22575334f92fba093603c0..c2892fa1d34ceaf89c2b5e8daae74f1909c57751 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -459,7 +459,7 @@ impl sum_tree::Item for Transform { impl sum_tree::Summary for TransformSummary { type Context = (); - fn add_summary(&mut self, other: &Self) { + fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { self.buffer += &other.buffer; self.display += &other.display; } @@ -467,7 +467,7 @@ impl sum_tree::Summary for TransformSummary { impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary { fn add_summary(&mut self, summary: &'a TransformSummary) { - sum_tree::Summary::add_summary(self, summary); + sum_tree::Summary::add_summary(self, summary, None); } } @@ -512,7 +512,7 @@ impl Default for FoldSummary { impl sum_tree::Summary for FoldSummary { type Context = Buffer; - fn add_summary_with_ctx(&mut self, other: &Self, buffer: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, buffer: Option<&Self::Context>) { let buffer = buffer.unwrap(); if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less { self.min_start = other.min_start.clone(); diff --git a/zed/src/operation_queue.rs b/zed/src/operation_queue.rs index 27b0550b7638b5e7eceb9aa1a23876961b2812d6..e70d91b6cb0cf056139d020e2e69f72212a18183 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/operation_queue.rs @@ -68,7 +68,7 @@ impl KeyedItem for T { impl Summary for OperationSummary { type Context = (); - fn add_summary(&mut self, other: &Self) { + fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { assert!(self.key < other.key); self.key = other.key; self.len += other.len; diff --git a/zed/src/sum_tree/cursor.rs b/zed/src/sum_tree/cursor.rs index 44647ceb37e6c00ba2f59b0998b4629cb4622399..d90702ab6e924114ff5fe8c8fd25f6d319342b2b 100644 --- a/zed/src/sum_tree/cursor.rs +++ b/zed/src/sum_tree/cursor.rs @@ -560,7 +560,7 @@ where slice_items_summary .as_mut() .unwrap() - .add_summary_with_ctx(item_summary, ctx); + .add_summary(item_summary, ctx); } SeekAggregate::Summary(summary) => { summary.add_summary(item_summary); @@ -679,7 +679,7 @@ where slice_items_summary .as_mut() .unwrap() - .add_summary_with_ctx(item_summary, ctx); + .add_summary(item_summary, ctx); slice_item_summaries.push(item_summary.clone()); } SeekAggregate::Summary(summary) => { diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index 0c85eaa8ef8b7146c64e39285ea789fe921ba173..375e2d7e2a53f7fd05bea747650bda62d5e8e54c 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -25,14 +25,7 @@ pub trait KeyedItem: Item { pub trait Summary: Default + Clone + fmt::Debug { type Context; - fn add_summary(&mut self, _summary: &Self) { - unimplemented!(); - } - - fn add_summary_with_ctx(&mut self, summary: &Self, ctx: Option<&Self::Context>) { - assert!(ctx.is_none()); - self.add_summary(summary); - } + fn add_summary(&mut self, summary: &Self, ctx: Option<&Self::Context>); } pub trait Dimension<'a, S: Summary>: Clone + fmt::Debug + Default { @@ -134,6 +127,13 @@ impl SumTree { } pub fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + self.extend_with_ctx(iter, None) + } + + pub fn extend_with_ctx(&mut self, iter: I, ctx: Option<&::Context>) where I: IntoIterator, { @@ -159,7 +159,7 @@ impl SumTree { }) = leaf.as_mut() { let item_summary = item.summary(); - summary.add_summary(&item_summary); + summary.add_summary(&item_summary, ctx); items.push(item); item_summaries.push(item_summary); } else { @@ -226,7 +226,7 @@ impl SumTree { .. } => { let other_node = other.0.clone(); - summary.add_summary_with_ctx(other_node.summary(), ctx); + summary.add_summary(other_node.summary(), ctx); let height_delta = *height - other_node.height(); let mut summaries_to_append = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new(); @@ -323,7 +323,7 @@ impl SumTree { item_summaries: right_summaries, }))) } else { - summary.add_summary_with_ctx(other_node.summary(), ctx); + summary.add_summary(other_node.summary(), ctx); items.extend(other_node.items().iter().cloned()); item_summaries.extend(other_node.child_summaries().iter().cloned()); None @@ -538,7 +538,7 @@ where { let mut sum = T::default(); for value in iter { - sum.add_summary_with_ctx(value, ctx); + sum.add_summary(value, ctx); } sum } @@ -889,7 +889,7 @@ mod tests { impl Summary for IntegersSummary { type Context = (); - fn add_summary(&mut self, other: &Self) { + fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { self.count.0 += &other.count.0; self.sum.0 += &other.sum.0; self.contains_even |= other.contains_even; diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index eb5172d57736647b5775bbe7d2249ebee13ba68a..2c16833395ce8ebb2c1c8a1b48454023afd31406 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -538,7 +538,7 @@ impl Default for EntrySummary { impl sum_tree::Summary for EntrySummary { type Context = (); - fn add_summary(&mut self, rhs: &Self) { + fn add_summary(&mut self, rhs: &Self, _: Option<&Self::Context>) { self.max_path = rhs.max_path.clone(); self.file_count += rhs.file_count; self.visible_file_count += rhs.visible_file_count; From 178705f8f97414b1c0f7e81d962120c84eed5f50 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 18:49:41 +0200 Subject: [PATCH 09/27] Add `FoldMap::folds_in_range` to randomized test and fix issues it found --- zed/src/editor/display_map/fold_map.rs | 80 +++++++---- zed/src/sum_tree/cursor.rs | 181 +++++++++---------------- zed/src/sum_tree/mod.rs | 2 +- 3 files changed, 120 insertions(+), 143 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index c2892fa1d34ceaf89c2b5e8daae74f1909c57751..c9e350cba86acddee31a677b5d5e7f010114af47 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -3,7 +3,7 @@ use super::{ Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset, }; use crate::{ - sum_tree::{self, Cursor, SeekBias, SumTree}, + sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree}, time, }; use anyhow::{anyhow, Result}; @@ -80,15 +80,7 @@ impl FoldMap { where T: ToOffset, { - let buffer = self.buffer.read(ctx); - let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?; - Ok(self - .folds - .filter::<_, usize>(move |summary| { - range.start.cmp(&summary.max_end, buffer).unwrap() < Ordering::Equal - && range.end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal - }) - .map(|f| &f.0)) + Ok(self.intersecting_folds(range, ctx)?.map(|f| &f.0)) } pub fn fold( @@ -150,25 +142,17 @@ impl FoldMap { let mut edits = Vec::new(); let mut fold_ixs_to_delete = Vec::new(); for range in ranges.into_iter() { - let start = buffer.anchor_before(range.start.to_offset(buffer)?)?; - let end = buffer.anchor_after(range.end.to_offset(buffer)?)?; - let range = start..end; - // Remove intersecting folds and add their ranges to edits that are passed to apply_edits - let mut cursor = self.folds.filter::<_, usize>(|summary| { - range.start.cmp(&summary.max_end, buffer).unwrap() < Ordering::Equal - && range.end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal - }); - - while let Some(fold) = cursor.item() { + let mut folds_cursor = self.intersecting_folds(range, ctx)?; + while let Some(fold) = folds_cursor.item() { let offset_range = fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap(); edits.push(Edit { old_range: offset_range.clone(), new_range: offset_range, }); - fold_ixs_to_delete.push(*cursor.start()); - cursor.next(); + fold_ixs_to_delete.push(*folds_cursor.start()); + folds_cursor.next(); } } fold_ixs_to_delete.sort_unstable(); @@ -192,6 +176,23 @@ impl FoldMap { Ok(()) } + fn intersecting_folds<'a, T>( + &self, + range: Range, + ctx: &'a AppContext, + ) -> Result bool, Fold, usize>> + where + T: ToOffset, + { + let buffer = self.buffer.read(ctx); + let start = buffer.anchor_before(range.start.to_offset(buffer)?)?; + let end = buffer.anchor_after(range.end.to_offset(buffer)?)?; + Ok(self.folds.filter::<_, usize>(move |summary| { + start.cmp(&summary.max_end, buffer).unwrap() <= Ordering::Equal + && end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal + })) + } + pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool { let transforms = self.sync(ctx); let mut cursor = transforms.cursor::(); @@ -502,7 +503,7 @@ impl Default for FoldSummary { Self { start: Anchor::Start, end: Anchor::End, - min_start: Anchor::Start, + min_start: Anchor::End, max_end: Anchor::Start, count: 0, } @@ -521,12 +522,12 @@ impl sum_tree::Summary for FoldSummary { self.max_end = other.max_end.clone(); } - if cfg!(debug_assertions) { + #[cfg(debug_assertions)] + { let start_comparison = self.start.cmp(&other.start, buffer).unwrap(); - let end_comparison = self.end.cmp(&other.end, buffer).unwrap(); assert!(start_comparison <= Ordering::Equal); if start_comparison == Ordering::Equal { - assert!(end_comparison >= Ordering::Equal); + assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal); } } self.start = other.start.clone(); @@ -821,6 +822,7 @@ mod tests { fold_ranges, vec![ Point::new(0, 2)..Point::new(2, 2), + Point::new(0, 4)..Point::new(1, 0), Point::new(1, 2)..Point::new(3, 2) ] ); @@ -848,7 +850,7 @@ mod tests { }; for seed in seed_range { - println!("{:?}", seed); + dbg!(seed); let mut rng = StdRng::seed_from_u64(seed); App::test((), |app| { @@ -973,6 +975,30 @@ mod tests { ); assert!(map.is_line_folded(display_point.row(), app.as_ref())); } + + for _ in 0..5 { + let end = rng.gen_range(0..=buffer.len()); + let start = rng.gen_range(0..=end); + let expected_folds = map + .folds + .items() + .into_iter() + .filter(|fold| { + let fold_start = fold.0.start.to_offset(buffer).unwrap(); + let fold_end = fold.0.end.to_offset(buffer).unwrap(); + start <= fold_end && end >= fold_start + }) + .map(|fold| fold.0) + .collect::>(); + + assert_eq!( + map.folds_in_range(start..end, app.as_ref()) + .unwrap() + .cloned() + .collect::>(), + expected_folds + ); + } } }); } diff --git a/zed/src/sum_tree/cursor.rs b/zed/src/sum_tree/cursor.rs index d90702ab6e924114ff5fe8c8fd25f6d319342b2b..67e585c1343c8443d0b274168606d88184221cfd 100644 --- a/zed/src/sum_tree/cursor.rs +++ b/zed/src/sum_tree/cursor.rs @@ -199,9 +199,6 @@ where } pub fn next(&mut self) { - if !self.did_seek { - self.descend_to_first_item(self.tree, |_| true) - } self.next_internal(|_| true) } @@ -209,67 +206,88 @@ where where F: Fn(&T::Summary) -> bool, { - assert!(self.did_seek, "Must seek before calling this method"); + let mut descend = false; - if self.stack.is_empty() { - if !self.at_end { - self.descend_to_first_item(self.tree, filter_node); - } - } else { - while self.stack.len() > 0 { - let new_subtree = { - let entry = self.stack.last_mut().unwrap(); - match entry.tree.0.as_ref() { - Node::Internal { - child_trees, - child_summaries, - .. - } => { - while entry.index < child_summaries.len() { - entry - .seek_dimension - .add_summary(&child_summaries[entry.index]); - entry - .sum_dimension - .add_summary(&child_summaries[entry.index]); - - entry.index += 1; - if let Some(next_summary) = child_summaries.get(entry.index) { - if filter_node(next_summary) { - break; - } else { - self.seek_dimension.add_summary(next_summary); - self.sum_dimension.add_summary(next_summary); - } - } - } + if !self.did_seek { + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + seek_dimension: S::default(), + sum_dimension: U::default(), + }); + descend = true; + self.did_seek = true; + } + + while self.stack.len() > 0 { + let new_subtree = { + let entry = self.stack.last_mut().unwrap(); + match entry.tree.0.as_ref() { + Node::Internal { + child_trees, + child_summaries, + .. + } => { + if !descend { + let summary = &child_summaries[entry.index]; + entry.seek_dimension.add_summary(summary); + entry.sum_dimension.add_summary(summary); + entry.index += 1; + } - child_trees.get(entry.index) + while entry.index < child_summaries.len() { + let next_summary = &child_summaries[entry.index]; + if filter_node(next_summary) { + break; + } else { + self.seek_dimension.add_summary(next_summary); + self.sum_dimension.add_summary(next_summary); + } + entry.index += 1; } - Node::Leaf { item_summaries, .. } => loop { + + child_trees.get(entry.index) + } + Node::Leaf { item_summaries, .. } => { + if !descend { let item_summary = &item_summaries[entry.index]; self.seek_dimension.add_summary(item_summary); entry.seek_dimension.add_summary(item_summary); self.sum_dimension.add_summary(item_summary); entry.sum_dimension.add_summary(item_summary); entry.index += 1; + } + + loop { if let Some(next_item_summary) = item_summaries.get(entry.index) { if filter_node(next_item_summary) { return; + } else { + self.seek_dimension.add_summary(next_item_summary); + entry.seek_dimension.add_summary(next_item_summary); + self.sum_dimension.add_summary(next_item_summary); + entry.sum_dimension.add_summary(next_item_summary); + entry.index += 1; } } else { break None; } - }, + } } - }; - - if let Some(subtree) = new_subtree { - self.descend_to_first_item(subtree, filter_node); - break; - } else { - self.stack.pop(); } + }; + + if let Some(subtree) = new_subtree { + descend = true; + self.stack.push(StackEntry { + tree: subtree, + index: 0, + seek_dimension: self.seek_dimension.clone(), + sum_dimension: self.sum_dimension.clone(), + }); + } else { + descend = false; + self.stack.pop(); } } @@ -277,67 +295,6 @@ where debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf()); } - pub fn descend_to_first_item(&mut self, mut subtree: &'a SumTree, filter_node: F) - where - F: Fn(&T::Summary) -> bool, - { - self.did_seek = true; - loop { - subtree = match *subtree.0 { - Node::Internal { - ref child_trees, - ref child_summaries, - .. - } => { - let mut new_index = None; - for (index, summary) in child_summaries.iter().enumerate() { - if filter_node(summary) { - new_index = Some(index); - break; - } - self.seek_dimension.add_summary(summary); - self.sum_dimension.add_summary(summary); - } - - if let Some(new_index) = new_index { - self.stack.push(StackEntry { - tree: subtree, - index: new_index, - seek_dimension: self.seek_dimension.clone(), - sum_dimension: self.sum_dimension.clone(), - }); - &child_trees[new_index] - } else { - break; - } - } - Node::Leaf { - ref item_summaries, .. - } => { - let mut new_index = None; - for (index, item_summary) in item_summaries.iter().enumerate() { - if filter_node(item_summary) { - new_index = Some(index); - break; - } - self.seek_dimension.add_summary(item_summary); - self.sum_dimension.add_summary(item_summary); - } - - if let Some(new_index) = new_index { - self.stack.push(StackEntry { - tree: subtree, - index: new_index, - seek_dimension: self.seek_dimension.clone(), - sum_dimension: self.sum_dimension.clone(), - }); - } - break; - } - } - } - } - fn descend_to_last_item(&mut self, mut subtree: &'a SumTree) { self.did_seek = true; loop { @@ -744,7 +701,7 @@ where fn next(&mut self) -> Option { if !self.did_seek { - self.descend_to_first_item(self.tree, |_| true); + self.next(); } if let Some(item) = self.item() { @@ -769,13 +726,7 @@ where { pub fn new(tree: &'a SumTree, filter_node: F) -> Self { let mut cursor = tree.cursor::<(), U>(); - if filter_node(&tree.summary()) { - cursor.descend_to_first_item(tree, &filter_node); - } else { - cursor.did_seek = true; - cursor.at_end = true; - } - + cursor.next_internal(&filter_node); Self { cursor, filter_node, diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index 375e2d7e2a53f7fd05bea747650bda62d5e8e54c..14ad59552f257546ca7a229fb5a81e8087df1909 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -73,7 +73,6 @@ impl SumTree { #[allow(unused)] pub fn items(&self) -> Vec { let mut cursor = self.cursor::<(), ()>(); - cursor.descend_to_first_item(self, |_| true); cursor.cloned().collect() } @@ -566,6 +565,7 @@ mod tests { for seed in 0..100 { use rand::{distributions, prelude::*}; + dbg!(seed); let rng = &mut StdRng::seed_from_u64(seed); let mut tree = SumTree::::new(); From 65e92ac2f2482d33fafbd4896ccd5370dcbb5a7b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 18:52:25 +0200 Subject: [PATCH 10/27] Ignore empty folds in `FoldMap::fold` Co-Authored-By: Nathan Sobo --- zed/src/editor/display_map/fold_map.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index c9e350cba86acddee31a677b5d5e7f010114af47..f79bf427c35216e2fab8ac9c72ad4519affe6561 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -94,11 +94,11 @@ impl FoldMap { let buffer = self.buffer.read(ctx); for range in ranges.into_iter() { let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?; - let fold = if range.start == range.end { - Fold(buffer.anchor_after(range.start)?..buffer.anchor_after(range.end)?) - } else { - Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?) - }; + if range.start == range.end { + continue; + } + + let fold = Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?); edits.push(Edit { old_range: range.clone(), new_range: range.clone(), From e8179f7e3e997c66d112d9de2b6606971f511ab9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 19:29:38 +0200 Subject: [PATCH 11/27] Always provide a context to sum tree's methods Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/mod.rs | 278 +++++++++++++++---------- zed/src/editor/buffer/text.rs | 22 +- zed/src/editor/display_map/fold_map.rs | 150 ++++++------- zed/src/operation_queue.rs | 5 +- zed/src/sum_tree/cursor.rs | 58 ++---- zed/src/sum_tree/mod.rs | 173 ++++++++------- zed/src/worktree.rs | 37 ++-- 7 files changed, 377 insertions(+), 346 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index bb796ace93ca0a6138369360a4825a06a96d7cb2..84c47ed6d7611810c7bcc9e4da3360cb943e3132 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -373,39 +373,48 @@ impl Buffer { insertion_splits.insert( base_insertion.id, - SumTree::from_item(InsertionSplit { - fragment_id: FragmentId::min_value().clone(), - extent: 0, - }), + SumTree::from_item( + InsertionSplit { + fragment_id: FragmentId::min_value().clone(), + extent: 0, + }, + &(), + ), + ); + fragments.push( + Fragment { + id: FragmentId::min_value().clone(), + insertion: base_insertion.clone(), + text: base_insertion.text.slice(0..0), + deletions: Default::default(), + max_undos: Default::default(), + visible: true, + }, + &(), ); - fragments.push(Fragment { - id: FragmentId::min_value().clone(), - insertion: base_insertion.clone(), - text: base_insertion.text.slice(0..0), - deletions: Default::default(), - max_undos: Default::default(), - visible: true, - }); if base_insertion.text.len() > 0 { let base_fragment_id = FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value()); - insertion_splits - .get_mut(&base_insertion.id) - .unwrap() - .push(InsertionSplit { + insertion_splits.get_mut(&base_insertion.id).unwrap().push( + InsertionSplit { fragment_id: base_fragment_id.clone(), extent: base_insertion.text.len(), - }); - fragments.push(Fragment { - id: base_fragment_id, - text: base_insertion.text.clone(), - insertion: base_insertion, - deletions: Default::default(), - max_undos: Default::default(), - visible: true, - }); + }, + &(), + ); + fragments.push( + Fragment { + id: base_fragment_id, + text: base_insertion.text.clone(), + insertion: base_insertion, + deletions: Default::default(), + max_undos: Default::default(), + visible: true, + }, + &(), + ); } Self { @@ -470,7 +479,7 @@ impl Buffer { let mut summary = TextSummary::default(); let mut cursor = self.fragments.cursor::(); - cursor.seek(&range.start, SeekBias::Right); + cursor.seek(&range.start, SeekBias::Right, &()); if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); @@ -480,7 +489,7 @@ impl Buffer { } if range.end > *cursor.start() { - summary += cursor.summary::(&range.end, SeekBias::Right); + summary += cursor.summary::(&range.end, SeekBias::Right, &()); if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); @@ -515,7 +524,7 @@ impl Buffer { let mut summary = TextSummary::default(); let mut cursor = self.fragments.cursor::(); - cursor.seek(&range.start, SeekBias::Right); + cursor.seek(&range.start, SeekBias::Right, &()); if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); @@ -525,7 +534,7 @@ impl Buffer { } if range.end > *cursor.start() { - summary += cursor.summary::(&range.end, SeekBias::Right); + summary += cursor.summary::(&range.end, SeekBias::Right, &()); if let Some(fragment) = cursor.item() { let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start(); @@ -933,10 +942,10 @@ impl Buffer { let mut cursor = old_fragments.cursor::(); let mut new_fragments = - cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left); + cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); if start_offset == cursor.item().unwrap().end_offset() { - new_fragments.push(cursor.item().unwrap().clone()); + new_fragments.push(cursor.item().unwrap().clone(), &()); cursor.next(); } @@ -975,30 +984,33 @@ impl Buffer { None }; if let Some(fragment) = before_range { - new_fragments.push(fragment); + new_fragments.push(fragment, &()); } if let Some(fragment) = insertion { - new_fragments.push(fragment); + new_fragments.push(fragment, &()); } if let Some(mut fragment) = within_range { if fragment.was_visible(&version_in_range, &self.undo_map) { fragment.deletions.insert(local_timestamp); fragment.visible = false; } - new_fragments.push(fragment); + new_fragments.push(fragment, &()); } if let Some(fragment) = after_range { - new_fragments.push(fragment); + new_fragments.push(fragment, &()); } } else { if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp { - new_fragments.push(self.build_fragment_to_insert( - cursor.prev_item().as_ref().unwrap(), - Some(&fragment), - new_text.take().unwrap(), - local_timestamp, - lamport_timestamp, - )); + new_fragments.push( + self.build_fragment_to_insert( + cursor.prev_item().as_ref().unwrap(), + Some(&fragment), + new_text.take().unwrap(), + local_timestamp, + lamport_timestamp, + ), + &(), + ); } if fragment.id < end_fragment_id @@ -1007,23 +1019,26 @@ impl Buffer { fragment.deletions.insert(local_timestamp); fragment.visible = false; } - new_fragments.push(fragment); + new_fragments.push(fragment, &()); } cursor.next(); } if let Some(new_text) = new_text { - new_fragments.push(self.build_fragment_to_insert( - cursor.prev_item().as_ref().unwrap(), - None, - new_text, - local_timestamp, - lamport_timestamp, - )); + new_fragments.push( + self.build_fragment_to_insert( + cursor.prev_item().as_ref().unwrap(), + None, + new_text, + local_timestamp, + lamport_timestamp, + ), + &(), + ); } - new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right)); + new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &()); self.fragments = new_fragments; self.local_clock.observe(local_timestamp); self.lamport_clock.observe(lamport_timestamp); @@ -1111,23 +1126,26 @@ impl Buffer { let mut insertion_splits = splits.cursor::<(), ()>().map(|s| &s.fragment_id).peekable(); let first_split_id = insertion_splits.next().unwrap(); - new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left); + new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &()); loop { let mut fragment = cursor.item().unwrap().clone(); fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); - new_fragments.push(fragment); + new_fragments.push(fragment, &()); cursor.next(); if let Some(split_id) = insertion_splits.next() { - new_fragments - .push_tree(cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left)); + new_fragments.push_tree( + cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()), + &(), + ); } else { break; } } } else { - new_fragments = cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left); + new_fragments = + cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); while let Some(fragment) = cursor.item() { if fragment.id > end_fragment_id { break; @@ -1139,13 +1157,13 @@ impl Buffer { fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); } - new_fragments.push(fragment); + new_fragments.push(fragment, &()); cursor.next(); } } } - new_fragments.push_tree(cursor.suffix()); + new_fragments.push_tree(cursor.suffix(&()), &()); drop(cursor); self.fragments = new_fragments; @@ -1209,7 +1227,7 @@ impl Buffer { .get(&edit_id) .ok_or_else(|| anyhow!("invalid operation"))?; let mut cursor = split_tree.cursor::(); - cursor.seek(&offset, SeekBias::Left); + cursor.seek(&offset, SeekBias::Left, &()); Ok(cursor .item() .ok_or_else(|| anyhow!("invalid operation"))? @@ -1231,7 +1249,10 @@ impl Buffer { let old_fragments = self.fragments.clone(); let mut cursor = old_fragments.cursor::(); let mut new_fragments = SumTree::new(); - new_fragments.push_tree(cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right)); + new_fragments.push_tree( + cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()), + &(), + ); let mut start_id = None; let mut start_offset = None; @@ -1253,7 +1274,8 @@ impl Buffer { .remove(&fragment.insertion.id) .unwrap(); let mut splits_cursor = old_split_tree.cursor::(); - let mut new_split_tree = splits_cursor.slice(&fragment.start_offset(), SeekBias::Right); + let mut new_split_tree = + splits_cursor.slice(&fragment.start_offset(), SeekBias::Right, &()); // Find all splices that start or end within the current fragment. Then, split the // fragment and reassemble it in both trees accounting for the deleted and the newly @@ -1266,11 +1288,14 @@ impl Buffer { prefix.id = FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id); fragment.set_start_offset(prefix.end_offset()); - new_fragments.push(prefix.clone()); - new_split_tree.push(InsertionSplit { - extent: prefix.end_offset() - prefix.start_offset(), - fragment_id: prefix.id, - }); + new_fragments.push(prefix.clone(), &()); + new_split_tree.push( + InsertionSplit { + extent: prefix.end_offset() - prefix.start_offset(), + fragment_id: prefix.id, + }, + &(), + ); fragment_start = range.start; } @@ -1294,7 +1319,7 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_fragments.push(new_fragment); + new_fragments.push(new_fragment, &()); } } @@ -1310,11 +1335,14 @@ impl Buffer { prefix.visible = false; } fragment.set_start_offset(prefix.end_offset()); - new_fragments.push(prefix.clone()); - new_split_tree.push(InsertionSplit { - extent: prefix.end_offset() - prefix.start_offset(), - fragment_id: prefix.id, - }); + new_fragments.push(prefix.clone(), &()); + new_split_tree.push( + InsertionSplit { + extent: prefix.end_offset() - prefix.start_offset(), + fragment_id: prefix.id, + }, + &(), + ); fragment_start = range.end; end_id = Some(fragment.insertion.id); end_offset = Some(fragment.start_offset()); @@ -1358,16 +1386,21 @@ impl Buffer { break; } } - new_split_tree.push(InsertionSplit { - extent: fragment.end_offset() - fragment.start_offset(), - fragment_id: fragment.id.clone(), - }); + new_split_tree.push( + InsertionSplit { + extent: fragment.end_offset() - fragment.start_offset(), + fragment_id: fragment.id.clone(), + }, + &(), + ); splits_cursor.next(); - new_split_tree - .push_tree(splits_cursor.slice(&old_split_tree.extent::(), SeekBias::Right)); + new_split_tree.push_tree( + splits_cursor.slice(&old_split_tree.extent::(), SeekBias::Right, &()), + &(), + ); self.insertion_splits .insert(fragment.insertion.id, new_split_tree); - new_fragments.push(fragment); + new_fragments.push(fragment, &()); // Scan forward until we find a fragment that is not fully contained by the current splice. cursor.next(); @@ -1383,7 +1416,7 @@ impl Buffer { new_fragment.deletions.insert(local_timestamp); new_fragment.visible = false; } - new_fragments.push(new_fragment); + new_fragments.push(new_fragment, &()); cursor.next(); if range.end == fragment_end { @@ -1425,7 +1458,8 @@ impl Buffer { // and push all the fragments in between into the new tree. if cur_range.as_ref().map_or(false, |r| r.start > fragment_end) { new_fragments.push_tree( - cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right), + cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()), + &(), ); } } @@ -1457,11 +1491,13 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_fragments.push(new_fragment); + new_fragments.push(new_fragment, &()); } } else { - new_fragments - .push_tree(cursor.slice(&old_fragments.extent::(), SeekBias::Right)); + new_fragments.push_tree( + cursor.slice(&old_fragments.extent::(), SeekBias::Right, &()), + &(), + ); } self.fragments = new_fragments; @@ -1519,32 +1555,43 @@ impl Buffer { .remove(&fragment.insertion.id) .unwrap(); let mut cursor = old_split_tree.cursor::(); - let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right); + let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right, &()); if let Some(ref fragment) = before_range { - new_split_tree.push(InsertionSplit { - extent: range.start - fragment.start_offset(), - fragment_id: fragment.id.clone(), - }); + new_split_tree.push( + InsertionSplit { + extent: range.start - fragment.start_offset(), + fragment_id: fragment.id.clone(), + }, + &(), + ); } if let Some(ref fragment) = within_range { - new_split_tree.push(InsertionSplit { - extent: range.end - range.start, - fragment_id: fragment.id.clone(), - }); + new_split_tree.push( + InsertionSplit { + extent: range.end - range.start, + fragment_id: fragment.id.clone(), + }, + &(), + ); } if let Some(ref fragment) = after_range { - new_split_tree.push(InsertionSplit { - extent: fragment.end_offset() - range.end, - fragment_id: fragment.id.clone(), - }); + new_split_tree.push( + InsertionSplit { + extent: fragment.end_offset() - range.end, + fragment_id: fragment.id.clone(), + }, + &(), + ); } cursor.next(); - new_split_tree - .push_tree(cursor.slice(&old_split_tree.extent::(), SeekBias::Right)); + new_split_tree.push_tree( + cursor.slice(&old_split_tree.extent::(), SeekBias::Right, &()), + &(), + ); self.insertion_splits .insert(fragment.insertion.id, new_split_tree); @@ -1569,10 +1616,13 @@ impl Buffer { ); let mut split_tree = SumTree::new(); - split_tree.push(InsertionSplit { - extent: text.len(), - fragment_id: new_fragment_id.clone(), - }); + split_tree.push( + InsertionSplit { + extent: text.len(), + fragment_id: new_fragment_id.clone(), + }, + &(), + ); self.insertion_splits.insert(local_timestamp, split_tree); Fragment::new( @@ -1621,7 +1671,7 @@ impl Buffer { }; let mut cursor = self.fragments.cursor::(); - cursor.seek(&offset, seek_bias); + cursor.seek(&offset, seek_bias, &()); let fragment = cursor.item().unwrap(); let offset_in_fragment = offset - cursor.start(); let offset_in_insertion = fragment.start_offset() + offset_in_fragment; @@ -1653,7 +1703,7 @@ impl Buffer { .get(&insertion_id) .ok_or_else(|| anyhow!("split does not exist for insertion id"))?; let mut splits_cursor = splits.cursor::(); - splits_cursor.seek(offset, seek_bias); + splits_cursor.seek(offset, seek_bias, &()); splits_cursor .item() .ok_or_else(|| anyhow!("split offset is out of range")) @@ -1681,13 +1731,13 @@ impl Buffer { .get(&insertion_id) .ok_or_else(|| anyhow!("split does not exist for insertion id"))?; let mut splits_cursor = splits.cursor::(); - splits_cursor.seek(offset, seek_bias); + splits_cursor.seek(offset, seek_bias, &()); let split = splits_cursor .item() .ok_or_else(|| anyhow!("split offset is out of range"))?; let mut fragments_cursor = self.fragments.cursor::(); - fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left); + fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &()); let fragment = fragments_cursor .item() .ok_or_else(|| anyhow!("fragment id does not exist"))?; @@ -1707,7 +1757,7 @@ impl Buffer { #[allow(dead_code)] pub fn point_for_offset(&self, offset: usize) -> Result { let mut fragments_cursor = self.fragments.cursor::(); - fragments_cursor.seek(&offset, SeekBias::Left); + fragments_cursor.seek(&offset, SeekBias::Left, &()); fragments_cursor .item() .ok_or_else(|| anyhow!("offset is out of range")) @@ -1772,7 +1822,7 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Point { impl<'a> CharIter<'a> { fn new(fragments: &'a SumTree, offset: usize) -> Self { let mut fragments_cursor = fragments.cursor::(); - fragments_cursor.seek(&offset, SeekBias::Right); + fragments_cursor.seek(&offset, SeekBias::Right, &()); let fragment_chars = fragments_cursor.item().map_or("".chars(), |fragment| { let offset_in_fragment = offset - fragments_cursor.start(); fragment.text[offset_in_fragment..].chars() @@ -1809,7 +1859,7 @@ impl<'a> Iterator for CharIter<'a> { impl<'a> FragmentIter<'a> { fn new(fragments: &'a SumTree) -> Self { let mut cursor = fragments.cursor::(); - cursor.seek(&0, SeekBias::Right); + cursor.seek(&0, SeekBias::Right, &()); Self { cursor, started: false, @@ -2105,7 +2155,7 @@ impl sum_tree::Item for Fragment { impl sum_tree::Summary for FragmentSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, _: &()) { self.text_summary += &other.text_summary; debug_assert!(self.max_fragment_id <= other.max_fragment_id); self.max_fragment_id = other.max_fragment_id.clone(); @@ -2171,7 +2221,7 @@ impl sum_tree::Item for InsertionSplit { impl sum_tree::Summary for InsertionSplitSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, _: &()) { self.extent += other.extent; } } @@ -2228,7 +2278,7 @@ pub trait ToOffset { impl ToOffset for Point { fn to_offset(&self, buffer: &Buffer) -> Result { let mut fragments_cursor = buffer.fragments.cursor::(); - fragments_cursor.seek(self, SeekBias::Left); + fragments_cursor.seek(self, SeekBias::Left, &()); fragments_cursor .item() .ok_or_else(|| anyhow!("point is out of range")) @@ -2272,7 +2322,7 @@ impl ToPoint for Anchor { impl ToPoint for usize { fn to_point(&self, buffer: &Buffer) -> Result { let mut fragments_cursor = buffer.fragments.cursor::(); - fragments_cursor.seek(&self, SeekBias::Left); + fragments_cursor.seek(&self, SeekBias::Left, &()); fragments_cursor .item() .ok_or_else(|| anyhow!("offset is out of range")) diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs index 140fe2257e324f462e2d9047dc9fc180479e7af9..03b8b528547c5abab29bd6e650835044a9ebe684 100644 --- a/zed/src/editor/buffer/text.rs +++ b/zed/src/editor/buffer/text.rs @@ -61,7 +61,7 @@ pub struct TextSummary { impl sum_tree::Summary for TextSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, _: &()) { *self += other; } } @@ -165,7 +165,7 @@ impl From> for Text { } let mut tree = SumTree::new(); - tree.extend(runs); + tree.extend(runs, &()); Text { text, runs: tree, @@ -239,13 +239,14 @@ impl Text { pub fn line_len(&self, row: u32) -> u32 { let mut cursor = self.runs.cursor::(); - cursor.seek(&self.range.start, SeekBias::Right); + cursor.seek(&self.range.start, SeekBias::Right, &()); let absolute_row = cursor.start().row + row; let mut cursor = self.runs.cursor::(); - cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right); + cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right, &()); let prefix_len = self.range.start.saturating_sub(*cursor.start()); - let line_len = cursor.summary::(&Point::new(absolute_row + 1, 0), SeekBias::Left); + let line_len = + cursor.summary::(&Point::new(absolute_row + 1, 0), SeekBias::Left, &()); let suffix_len = cursor.start().saturating_sub(self.range.end); line_len @@ -270,14 +271,15 @@ impl Text { candidates.push(Point::new(0, self.line_len(0))); if lines.row > 1 { let mut cursor = self.runs.cursor::(); - cursor.seek(&self.range.start, SeekBias::Right); + cursor.seek(&self.range.start, SeekBias::Right, &()); let absolute_start_row = cursor.start().row; let mut cursor = self.runs.cursor::(); - cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right); + cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right, &()); let summary = cursor.summary::( &Point::new(absolute_start_row + lines.row, 0), SeekBias::Left, + &(), ); candidates.push(Point::new(1, 0) + &summary.rightmost_point); @@ -295,7 +297,7 @@ impl Text { pub fn offset_for_point(&self, point: Point) -> usize { let mut cursor = self.runs.cursor::(); let abs_point = self.abs_point_for_offset(self.range.start) + &point; - cursor.seek(&abs_point, SeekBias::Right); + cursor.seek(&abs_point, SeekBias::Right, &()); let overshoot = abs_point - &cursor.start().lines; let abs_offset = cursor.start().chars + overshoot.column as usize; abs_offset - self.range.start @@ -315,14 +317,14 @@ impl Text { fn abs_point_for_offset(&self, offset: usize) -> Point { let mut cursor = self.runs.cursor::(); - cursor.seek(&offset, SeekBias::Right); + cursor.seek(&offset, SeekBias::Right, &()); let overshoot = (offset - cursor.start().chars) as u32; cursor.start().lines + &Point::new(0, overshoot) } fn abs_byte_offset_for_offset(&self, offset: usize) -> usize { let mut cursor = self.runs.cursor::(); - cursor.seek(&offset, SeekBias::Right); + cursor.seek(&offset, SeekBias::Right, &()); let overshoot = offset - cursor.start().chars; cursor.start().bytes + overshoot * cursor.item().map_or(0, |run| run.char_size()) as usize } diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index f79bf427c35216e2fab8ac9c72ad4519affe6561..910f33558269f3c91499636d81ff49c845a770df 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -29,13 +29,16 @@ impl FoldMap { Self { buffer: buffer_handle, folds: Default::default(), - transforms: Mutex::new(SumTree::from_item(Transform { - summary: TransformSummary { - buffer: text_summary.clone(), - display: text_summary, + transforms: Mutex::new(SumTree::from_item( + Transform { + summary: TransformSummary { + buffer: text_summary.clone(), + display: text_summary, + }, + display_text: None, }, - display_text: None, - })), + &(), + )), last_sync: Mutex::new(buffer.version()), } } @@ -106,16 +109,12 @@ impl FoldMap { self.folds = { let mut new_tree = SumTree::new(); let mut cursor = self.folds.cursor::<_, ()>(); - new_tree.push_tree_with_ctx( - cursor.slice_with_ctx( - &FoldRange(fold.0.clone()), - SeekBias::Right, - Some(buffer), - ), - Some(buffer), + new_tree.push_tree( + cursor.slice(&FoldRange(fold.0.clone()), SeekBias::Right, buffer), + buffer, ); - new_tree.push_with_ctx(fold, Some(buffer)); - new_tree.push_tree_with_ctx(cursor.suffix_with_ctx(Some(buffer)), Some(buffer)); + new_tree.push(fold, buffer); + new_tree.push_tree(cursor.suffix(buffer), buffer); new_tree }; } @@ -162,13 +161,10 @@ impl FoldMap { let mut cursor = self.folds.cursor::<_, ()>(); let mut folds = SumTree::new(); for fold_ix in fold_ixs_to_delete { - folds.push_tree_with_ctx( - cursor.slice_with_ctx(&fold_ix, SeekBias::Right, Some(buffer)), - Some(buffer), - ); + folds.push_tree(cursor.slice(&fold_ix, SeekBias::Right, buffer), buffer); cursor.next(); } - folds.push_tree_with_ctx(cursor.suffix_with_ctx(Some(buffer)), Some(buffer)); + folds.push_tree(cursor.suffix(buffer), buffer); folds }; @@ -196,7 +192,7 @@ impl FoldMap { pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool { let transforms = self.sync(ctx); let mut cursor = transforms.cursor::(); - cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right); + cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &()); while let Some(transform) = cursor.item() { if transform.display_text.is_some() { return true; @@ -213,7 +209,7 @@ impl FoldMap { pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result { let transforms = self.sync(ctx); let mut cursor = transforms.cursor::(); - cursor.seek(&point, SeekBias::Right); + cursor.seek(&point, SeekBias::Right, &()); let overshoot = point.0 - cursor.start().display.lines; (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx)) } @@ -229,7 +225,7 @@ impl FoldMap { pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point { let transforms = self.sync(ctx); let mut cursor = transforms.cursor::(); - cursor.seek(&display_point, SeekBias::Right); + cursor.seek(&display_point, SeekBias::Right, &()); let overshoot = display_point.0 - cursor.start().display.lines; cursor.start().buffer.lines + overshoot } @@ -237,7 +233,7 @@ impl FoldMap { pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint { let transforms = self.sync(ctx); let mut cursor = transforms.cursor::(); - cursor.seek(&point, SeekBias::Right); + cursor.seek(&point, SeekBias::Right, &()); let overshoot = point - cursor.start().buffer.lines; DisplayPoint(cmp::min( cursor.start().display.lines + overshoot, @@ -262,14 +258,17 @@ impl FoldMap { let mut new_transforms = SumTree::new(); let mut transforms = self.transforms.lock(); let mut cursor = transforms.cursor::(); - cursor.seek(&0, SeekBias::Right); + cursor.seek(&0, SeekBias::Right, &()); while let Some(mut edit) = edits.next() { - new_transforms.push_tree(cursor.slice(&edit.old_range.start, SeekBias::Left)); + new_transforms.push_tree( + cursor.slice(&edit.old_range.start, SeekBias::Left, &()), + &(), + ); edit.new_range.start -= edit.old_range.start - cursor.start(); edit.old_range.start = *cursor.start(); - cursor.seek(&edit.old_range.end, SeekBias::Right); + cursor.seek(&edit.old_range.end, SeekBias::Right, &()); cursor.next(); let mut delta = edit.delta(); @@ -286,7 +285,7 @@ impl FoldMap { if next_edit.old_range.end >= edit.old_range.end { edit.old_range.end = next_edit.old_range.end; - cursor.seek(&edit.old_range.end, SeekBias::Right); + cursor.seek(&edit.old_range.end, SeekBias::Right, &()); cursor.next(); } } else { @@ -299,11 +298,7 @@ impl FoldMap { let anchor = buffer.anchor_before(edit.new_range.start).unwrap(); let mut folds_cursor = self.folds.cursor::<_, ()>(); - folds_cursor.seek_with_ctx( - &FoldRange(anchor..Anchor::End), - SeekBias::Left, - Some(buffer), - ); + folds_cursor.seek(&FoldRange(anchor..Anchor::End), SeekBias::Left, buffer); let mut folds = folds_cursor .map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap()) .peekable(); @@ -329,29 +324,35 @@ impl FoldMap { if fold.start > sum.buffer.chars { let text_summary = buffer.text_summary_for_range(sum.buffer.chars..fold.start); - new_transforms.push(Transform { - summary: TransformSummary { - display: text_summary.clone(), - buffer: text_summary, + new_transforms.push( + Transform { + summary: TransformSummary { + display: text_summary.clone(), + buffer: text_summary, + }, + display_text: None, }, - display_text: None, - }); + &(), + ); } if fold.end > fold.start { - new_transforms.push(Transform { - summary: TransformSummary { - display: TextSummary { - chars: 1, - bytes: '…'.len_utf8(), - lines: Point::new(0, 1), - first_line_len: 1, - rightmost_point: Point::new(0, 1), + new_transforms.push( + Transform { + summary: TransformSummary { + display: TextSummary { + chars: 1, + bytes: '…'.len_utf8(), + lines: Point::new(0, 1), + first_line_len: 1, + rightmost_point: Point::new(0, 1), + }, + buffer: buffer.text_summary_for_range(fold.start..fold.end), }, - buffer: buffer.text_summary_for_range(fold.start..fold.end), + display_text: Some('…'), }, - display_text: Some('…'), - }); + &(), + ); } } @@ -359,26 +360,32 @@ impl FoldMap { if sum.buffer.chars < edit.new_range.end { let text_summary = buffer.text_summary_for_range(sum.buffer.chars..edit.new_range.end); - new_transforms.push(Transform { - summary: TransformSummary { - display: text_summary.clone(), - buffer: text_summary, + new_transforms.push( + Transform { + summary: TransformSummary { + display: text_summary.clone(), + buffer: text_summary, + }, + display_text: None, }, - display_text: None, - }); + &(), + ); } } - new_transforms.push_tree(cursor.suffix()); + new_transforms.push_tree(cursor.suffix(&()), &()); if new_transforms.is_empty() { let text_summary = buffer.text_summary(); - new_transforms.push(Transform { - summary: TransformSummary { - display: text_summary.clone(), - buffer: text_summary, + new_transforms.push( + Transform { + summary: TransformSummary { + display: text_summary.clone(), + buffer: text_summary, + }, + display_text: None, }, - display_text: None, - }); + &(), + ); } drop(cursor); @@ -399,7 +406,7 @@ impl FoldMapSnapshot { let display_point = Point::new(start_row, 0); let mut cursor = self.transforms.cursor(); - cursor.seek(&DisplayPoint(display_point), SeekBias::Left); + cursor.seek(&DisplayPoint(display_point), SeekBias::Left, &()); Ok(BufferRows { display_point, @@ -410,7 +417,7 @@ impl FoldMapSnapshot { pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Result> { let offset = self.to_display_offset(point, ctx)?; let mut cursor = self.transforms.cursor(); - cursor.seek(&offset, SeekBias::Right); + cursor.seek(&offset, SeekBias::Right, &()); Ok(Chars { cursor, offset: offset.0, @@ -421,7 +428,7 @@ impl FoldMapSnapshot { fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result { let mut cursor = self.transforms.cursor::(); - cursor.seek(&point, SeekBias::Right); + cursor.seek(&point, SeekBias::Right, &()); let overshoot = point.0 - cursor.start().display.lines; let mut offset = cursor.start().display.chars; if !overshoot.is_zero() { @@ -460,7 +467,7 @@ impl sum_tree::Item for Transform { impl sum_tree::Summary for TransformSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, _: &()) { self.buffer += &other.buffer; self.display += &other.display; } @@ -468,7 +475,7 @@ impl sum_tree::Summary for TransformSummary { impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary { fn add_summary(&mut self, summary: &'a TransformSummary) { - sum_tree::Summary::add_summary(self, summary, None); + sum_tree::Summary::add_summary(self, summary, &()); } } @@ -513,8 +520,7 @@ impl Default for FoldSummary { impl sum_tree::Summary for FoldSummary { type Context = Buffer; - fn add_summary(&mut self, other: &Self, buffer: Option<&Self::Context>) { - let buffer = buffer.unwrap(); + fn add_summary(&mut self, other: &Self, buffer: &Buffer) { if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less { self.min_start = other.min_start.clone(); } @@ -553,8 +559,8 @@ impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange { } impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for FoldRange { - fn cmp(&self, other: &Self, buffer: Option<&Buffer>) -> Ordering { - self.0.cmp(&other.0, buffer.unwrap()).unwrap() + fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering { + self.0.cmp(&other.0, buffer).unwrap() } } diff --git a/zed/src/operation_queue.rs b/zed/src/operation_queue.rs index e70d91b6cb0cf056139d020e2e69f72212a18183..2c0e234fe98cd30c6d3fa72b210a93c0441b8249 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/operation_queue.rs @@ -32,7 +32,8 @@ impl OperationQueue { pub fn insert(&mut self, mut ops: Vec) { ops.sort_by_key(|op| op.timestamp()); ops.dedup_by_key(|op| op.timestamp()); - self.0.edit(ops.into_iter().map(Edit::Insert).collect()); + self.0 + .edit(ops.into_iter().map(Edit::Insert).collect(), &()); } pub fn drain(&mut self) -> Self { @@ -68,7 +69,7 @@ impl KeyedItem for T { impl Summary for OperationSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, _: &()) { assert!(self.key < other.key); self.key = other.key; self.len += other.len; diff --git a/zed/src/sum_tree/cursor.rs b/zed/src/sum_tree/cursor.rs index 67e585c1343c8443d0b274168606d88184221cfd..3d65d3f9c34d7940a6e0b2abd597fa97fce1a573 100644 --- a/zed/src/sum_tree/cursor.rs +++ b/zed/src/sum_tree/cursor.rs @@ -208,7 +208,7 @@ where { let mut descend = false; - if !self.did_seek { + if self.stack.is_empty() && !self.at_end { self.stack.push(StackEntry { tree: self.tree, index: 0, @@ -342,42 +342,30 @@ where S: SeekDimension<'a, T::Summary>, U: Dimension<'a, T::Summary>, { - pub fn seek(&mut self, pos: &S, bias: SeekBias) -> bool { - self.seek_with_ctx(pos, bias, None) - } - - pub fn seek_with_ctx( + pub fn seek( &mut self, pos: &S, bias: SeekBias, - ctx: Option<&'a ::Context>, + ctx: &::Context, ) -> bool { self.reset(); self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, ctx) } - pub fn seek_forward(&mut self, pos: &S, bias: SeekBias) -> bool { - self.seek_forward_with_ctx(pos, bias, None) - } - - pub fn seek_forward_with_ctx( + pub fn seek_forward( &mut self, pos: &S, bias: SeekBias, - ctx: Option<&'a ::Context>, + ctx: &::Context, ) -> bool { self.seek_internal::<()>(pos, bias, &mut SeekAggregate::None, ctx) } - pub fn slice(&mut self, end: &S, bias: SeekBias) -> SumTree { - self.slice_with_ctx(end, bias, None) - } - - pub fn slice_with_ctx( + pub fn slice( &mut self, end: &S, bias: SeekBias, - ctx: Option<&'a ::Context>, + ctx: &::Context, ) -> SumTree { let mut slice = SeekAggregate::Slice(SumTree::new()); self.seek_internal::<()>(end, bias, &mut slice, ctx); @@ -388,14 +376,7 @@ where } } - pub fn suffix(&mut self) -> SumTree { - self.suffix_with_ctx(None) - } - - pub fn suffix_with_ctx( - &mut self, - ctx: Option<&'a ::Context>, - ) -> SumTree { + pub fn suffix(&mut self, ctx: &::Context) -> SumTree { let extent = self.tree.extent::(); let mut slice = SeekAggregate::Slice(SumTree::new()); self.seek_internal::<()>(&extent, SeekBias::Right, &mut slice, ctx); @@ -406,18 +387,11 @@ where } } - pub fn summary(&mut self, end: &S, bias: SeekBias) -> D - where - D: Dimension<'a, T::Summary>, - { - self.summary_with_ctx(end, bias, None) - } - - pub fn summary_with_ctx( + pub fn summary( &mut self, end: &S, bias: SeekBias, - ctx: Option<&'a ::Context>, + ctx: &::Context, ) -> D where D: Dimension<'a, T::Summary>, @@ -436,7 +410,7 @@ where target: &S, bias: SeekBias, aggregate: &mut SeekAggregate, - ctx: Option<&'a ::Context>, + ctx: &::Context, ) -> bool where D: Dimension<'a, T::Summary>, @@ -470,7 +444,7 @@ where match aggregate { SeekAggregate::None => {} SeekAggregate::Slice(slice) => { - slice.push_tree_with_ctx(child_tree.clone(), ctx); + slice.push_tree(child_tree.clone(), ctx); } SeekAggregate::Summary(summary) => { summary.add_summary(child_summary); @@ -526,7 +500,7 @@ where entry.index += 1; } else { if let SeekAggregate::Slice(slice) = aggregate { - slice.push_tree_with_ctx( + slice.push_tree( SumTree(Arc::new(Node::Leaf { summary: slice_items_summary.unwrap(), items: slice_items, @@ -541,7 +515,7 @@ where if let SeekAggregate::Slice(slice) = aggregate { if !slice_items.is_empty() { - slice.push_tree_with_ctx( + slice.push_tree( SumTree(Arc::new(Node::Leaf { summary: slice_items_summary.unwrap(), items: slice_items, @@ -586,7 +560,7 @@ where match aggregate { SeekAggregate::None => {} SeekAggregate::Slice(slice) => { - slice.push_tree_with_ctx(child_trees[index].clone(), ctx); + slice.push_tree(child_trees[index].clone(), ctx); } SeekAggregate::Summary(summary) => { summary.add_summary(child_summary); @@ -656,7 +630,7 @@ where if let SeekAggregate::Slice(slice) = aggregate { if !slice_items.is_empty() { - slice.push_tree_with_ctx( + slice.push_tree( SumTree(Arc::new(Node::Leaf { summary: slice_items_summary.unwrap(), items: slice_items, diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index 14ad59552f257546ca7a229fb5a81e8087df1909..e70440f16f0c0bfeb811c8ad1aa6167adc85b253 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -25,7 +25,7 @@ pub trait KeyedItem: Item { pub trait Summary: Default + Clone + fmt::Debug { type Context; - fn add_summary(&mut self, summary: &Self, ctx: Option<&Self::Context>); + fn add_summary(&mut self, summary: &Self, ctx: &Self::Context); } pub trait Dimension<'a, S: Summary>: Clone + fmt::Debug + Default { @@ -37,11 +37,11 @@ impl<'a, T: Summary> Dimension<'a, T> for () { } pub trait SeekDimension<'a, T: Summary>: Dimension<'a, T> { - fn cmp(&self, other: &Self, ctx: Option<&T::Context>) -> Ordering; + fn cmp(&self, other: &Self, ctx: &T::Context) -> Ordering; } impl<'a, S: Summary, T: Dimension<'a, S> + Ord> SeekDimension<'a, S> for T { - fn cmp(&self, other: &Self, _ctx: Option<&S::Context>) -> Ordering { + fn cmp(&self, other: &Self, _ctx: &S::Context) -> Ordering { Ord::cmp(self, other) } } @@ -64,9 +64,9 @@ impl SumTree { })) } - pub fn from_item(item: T) -> Self { + pub fn from_item(item: T, ctx: &::Context) -> Self { let mut tree = Self::new(); - tree.push(item); + tree.push(item, ctx); tree } @@ -125,14 +125,7 @@ impl SumTree { } } - pub fn extend(&mut self, iter: I) - where - I: IntoIterator, - { - self.extend_with_ctx(iter, None) - } - - pub fn extend_with_ctx(&mut self, iter: I, ctx: Option<&::Context>) + pub fn extend(&mut self, iter: I, ctx: &::Context) where I: IntoIterator, { @@ -140,7 +133,7 @@ impl SumTree { for item in iter { if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE { - self.push_tree(SumTree(Arc::new(leaf.take().unwrap()))); + self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); } if leaf.is_none() { @@ -167,17 +160,13 @@ impl SumTree { } if leaf.is_some() { - self.push_tree(SumTree(Arc::new(leaf.take().unwrap()))); + self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); } } - pub fn push(&mut self, item: T) { - self.push_with_ctx(item, None); - } - - pub fn push_with_ctx(&mut self, item: T, ctx: Option<&::Context>) { + pub fn push(&mut self, item: T, ctx: &::Context) { let summary = item.summary(); - self.push_tree_with_ctx( + self.push_tree( SumTree::from_child_trees( vec![SumTree(Arc::new(Node::Leaf { summary: summary.clone(), @@ -190,20 +179,12 @@ impl SumTree { ) } - pub fn push_tree(&mut self, other: Self) { - self.push_tree_with_ctx(other, None); - } - - pub fn push_tree_with_ctx( - &mut self, - other: Self, - ctx: Option<&::Context>, - ) { + pub fn push_tree(&mut self, other: Self, ctx: &::Context) { let other_node = other.0.clone(); if !other_node.is_leaf() || other_node.items().len() > 0 { if self.0.height() < other_node.height() { for tree in other_node.child_trees() { - self.push_tree_with_ctx(tree.clone(), ctx); + self.push_tree(tree.clone(), ctx); } } else if let Some(split_tree) = self.push_tree_recursive(other, ctx) { *self = Self::from_child_trees(vec![self.clone(), split_tree], ctx); @@ -214,7 +195,7 @@ impl SumTree { fn push_tree_recursive( &mut self, other: SumTree, - ctx: Option<&::Context>, + ctx: &::Context, ) -> Option> { match Arc::make_mut(&mut self.0) { Node::Internal { @@ -333,7 +314,7 @@ impl SumTree { fn from_child_trees( child_trees: Vec>, - ctx: Option<&::Context>, + ctx: &::Context, ) -> Self { let height = child_trees[0].0.height() + 1; let mut child_summaries = ArrayVec::new(); @@ -370,17 +351,21 @@ impl SumTree { impl SumTree { #[allow(unused)] - pub fn insert(&mut self, item: T) { + pub fn insert(&mut self, item: T, ctx: &::Context) { *self = { let mut cursor = self.cursor::(); - let mut new_tree = cursor.slice(&item.key(), SeekBias::Left); - new_tree.push(item); - new_tree.push_tree(cursor.suffix()); + let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, ctx); + new_tree.push(item, ctx); + new_tree.push_tree(cursor.suffix(ctx), ctx); new_tree }; } - pub fn edit(&mut self, mut edits: Vec>) -> Vec { + pub fn edit( + &mut self, + mut edits: Vec>, + ctx: &::Context, + ) -> Vec { if edits.is_empty() { return Vec::new(); } @@ -393,7 +378,7 @@ impl SumTree { let mut new_tree = SumTree::new(); let mut buffered_items = Vec::new(); - cursor.seek(&T::Key::default(), SeekBias::Left); + cursor.seek(&T::Key::default(), SeekBias::Left, ctx); for edit in edits { let new_key = edit.key(); let mut old_item = cursor.item(); @@ -402,9 +387,9 @@ impl SumTree { .as_ref() .map_or(false, |old_item| old_item.key() < new_key) { - new_tree.extend(buffered_items.drain(..)); - let slice = cursor.slice(&new_key, SeekBias::Left); - new_tree.push_tree(slice); + new_tree.extend(buffered_items.drain(..), ctx); + let slice = cursor.slice(&new_key, SeekBias::Left, ctx); + new_tree.push_tree(slice, ctx); old_item = cursor.item(); } @@ -423,17 +408,17 @@ impl SumTree { } } - new_tree.extend(buffered_items); - new_tree.push_tree(cursor.suffix()); + new_tree.extend(buffered_items, ctx); + new_tree.push_tree(cursor.suffix(ctx), ctx); new_tree }; removed } - pub fn get(&self, key: &T::Key) -> Option<&T> { + pub fn get(&self, key: &T::Key, ctx: &::Context) -> Option<&T> { let mut cursor = self.cursor::(); - if cursor.seek(key, SeekBias::Left) { + if cursor.seek(key, SeekBias::Left, ctx) { cursor.item() } else { None @@ -530,7 +515,7 @@ impl Edit { } } -fn sum<'a, T, I>(iter: I, ctx: Option<&T::Context>) -> T +fn sum<'a, T, I>(iter: I, ctx: &T::Context) -> T where T: 'a + Summary, I: Iterator, @@ -551,12 +536,12 @@ mod tests { #[test] fn test_extend_and_push_tree() { let mut tree1 = SumTree::new(); - tree1.extend(0..20); + tree1.extend(0..20, &()); let mut tree2 = SumTree::new(); - tree2.extend(50..100); + tree2.extend(50..100, &()); - tree1.push_tree(tree2); + tree1.push_tree(tree2, &()); assert_eq!(tree1.items(), (0..20).chain(50..100).collect::>()); } @@ -570,7 +555,7 @@ mod tests { let mut tree = SumTree::::new(); let count = rng.gen_range(0..10); - tree.extend(rng.sample_iter(distributions::Standard).take(count)); + tree.extend(rng.sample_iter(distributions::Standard).take(count), &()); for _ in 0..5 { let splice_end = rng.gen_range(0..tree.extent::().0 + 1); @@ -587,10 +572,10 @@ mod tests { tree = { let mut cursor = tree.cursor::(); - let mut new_tree = cursor.slice(&Count(splice_start), SeekBias::Right); - new_tree.extend(new_items); - cursor.seek(&Count(splice_end), SeekBias::Right); - new_tree.push_tree(cursor.slice(&tree_end, SeekBias::Right)); + let mut new_tree = cursor.slice(&Count(splice_start), SeekBias::Right, &()); + new_tree.extend(new_items, &()); + cursor.seek(&Count(splice_end), SeekBias::Right, &()); + new_tree.push_tree(cursor.slice(&tree_end, SeekBias::Right, &()), &()); new_tree }; @@ -613,7 +598,7 @@ mod tests { let mut pos = rng.gen_range(0..tree.extent::().0 + 1); let mut before_start = false; let mut cursor = tree.cursor::(); - cursor.seek(&Count(pos), SeekBias::Right); + cursor.seek(&Count(pos), SeekBias::Right, &()); for i in 0..10 { assert_eq!(cursor.start().0, pos); @@ -661,11 +646,11 @@ mod tests { }; let mut cursor = tree.cursor::(); - cursor.seek(&Count(start), start_bias); - let slice = cursor.slice(&Count(end), end_bias); + cursor.seek(&Count(start), start_bias, &()); + let slice = cursor.slice(&Count(end), end_bias, &()); - cursor.seek(&Count(start), start_bias); - let summary = cursor.summary::(&Count(end), end_bias); + cursor.seek(&Count(start), start_bias, &()); + let summary = cursor.summary::(&Count(end), end_bias, &()); assert_eq!(summary, slice.summary().sum); } @@ -678,7 +663,7 @@ mod tests { let tree = SumTree::::new(); let mut cursor = tree.cursor::(); assert_eq!( - cursor.slice(&Count(0), SeekBias::Right).items(), + cursor.slice(&Count(0), SeekBias::Right, &()).items(), Vec::::new() ); assert_eq!(cursor.item(), None); @@ -687,10 +672,10 @@ mod tests { // Single-element tree let mut tree = SumTree::::new(); - tree.extend(vec![1]); + tree.extend(vec![1], &()); let mut cursor = tree.cursor::(); assert_eq!( - cursor.slice(&Count(0), SeekBias::Right).items(), + cursor.slice(&Count(0), SeekBias::Right, &()).items(), Vec::::new() ); assert_eq!(cursor.item(), Some(&1)); @@ -708,15 +693,15 @@ mod tests { assert_eq!(cursor.start(), &Sum(0)); let mut cursor = tree.cursor::(); - assert_eq!(cursor.slice(&Count(1), SeekBias::Right).items(), [1]); + assert_eq!(cursor.slice(&Count(1), SeekBias::Right, &()).items(), [1]); assert_eq!(cursor.item(), None); assert_eq!(cursor.prev_item(), Some(&1)); assert_eq!(cursor.start(), &Sum(1)); - cursor.seek(&Count(0), SeekBias::Right); + cursor.seek(&Count(0), SeekBias::Right, &()); assert_eq!( cursor - .slice(&tree.extent::(), SeekBias::Right) + .slice(&tree.extent::(), SeekBias::Right, &()) .items(), [1] ); @@ -726,10 +711,13 @@ mod tests { // Multiple-element tree let mut tree = SumTree::new(); - tree.extend(vec![1, 2, 3, 4, 5, 6]); + tree.extend(vec![1, 2, 3, 4, 5, 6], &()); let mut cursor = tree.cursor::(); - assert_eq!(cursor.slice(&Count(2), SeekBias::Right).items(), [1, 2]); + assert_eq!( + cursor.slice(&Count(2), SeekBias::Right, &()).items(), + [1, 2] + ); assert_eq!(cursor.item(), Some(&3)); assert_eq!(cursor.prev_item(), Some(&2)); assert_eq!(cursor.start(), &Sum(3)); @@ -798,7 +786,7 @@ mod tests { let mut cursor = tree.cursor::(); assert_eq!( cursor - .slice(&tree.extent::(), SeekBias::Right) + .slice(&tree.extent::(), SeekBias::Right, &()) .items(), tree.items() ); @@ -806,10 +794,10 @@ mod tests { assert_eq!(cursor.prev_item(), Some(&6)); assert_eq!(cursor.start(), &Sum(21)); - cursor.seek(&Count(3), SeekBias::Right); + cursor.seek(&Count(3), SeekBias::Right, &()); assert_eq!( cursor - .slice(&tree.extent::(), SeekBias::Right) + .slice(&tree.extent::(), SeekBias::Right, &()) .items(), [4, 5, 6] ); @@ -818,37 +806,46 @@ mod tests { assert_eq!(cursor.start(), &Sum(21)); // Seeking can bias left or right - cursor.seek(&Count(1), SeekBias::Left); + cursor.seek(&Count(1), SeekBias::Left, &()); assert_eq!(cursor.item(), Some(&1)); - cursor.seek(&Count(1), SeekBias::Right); + cursor.seek(&Count(1), SeekBias::Right, &()); assert_eq!(cursor.item(), Some(&2)); // Slicing without resetting starts from where the cursor is parked at. - cursor.seek(&Count(1), SeekBias::Right); - assert_eq!(cursor.slice(&Count(3), SeekBias::Right).items(), vec![2, 3]); - assert_eq!(cursor.slice(&Count(6), SeekBias::Left).items(), vec![4, 5]); - assert_eq!(cursor.slice(&Count(6), SeekBias::Right).items(), vec![6]); + cursor.seek(&Count(1), SeekBias::Right, &()); + assert_eq!( + cursor.slice(&Count(3), SeekBias::Right, &()).items(), + vec![2, 3] + ); + assert_eq!( + cursor.slice(&Count(6), SeekBias::Left, &()).items(), + vec![4, 5] + ); + assert_eq!( + cursor.slice(&Count(6), SeekBias::Right, &()).items(), + vec![6] + ); } #[test] fn test_edit() { let mut tree = SumTree::::new(); - let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)]); + let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)], &()); assert_eq!(tree.items(), vec![0, 1, 2]); assert_eq!(removed, Vec::::new()); - assert_eq!(tree.get(&0), Some(&0)); - assert_eq!(tree.get(&1), Some(&1)); - assert_eq!(tree.get(&2), Some(&2)); - assert_eq!(tree.get(&4), None); + assert_eq!(tree.get(&0, &()), Some(&0)); + assert_eq!(tree.get(&1, &()), Some(&1)); + assert_eq!(tree.get(&2, &()), Some(&2)); + assert_eq!(tree.get(&4, &()), None); - let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)]); + let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)], &()); assert_eq!(tree.items(), vec![1, 2, 4]); assert_eq!(removed, vec![0, 2]); - assert_eq!(tree.get(&0), None); - assert_eq!(tree.get(&1), Some(&1)); - assert_eq!(tree.get(&2), Some(&2)); - assert_eq!(tree.get(&4), Some(&4)); + assert_eq!(tree.get(&0, &()), None); + assert_eq!(tree.get(&1, &()), Some(&1)); + assert_eq!(tree.get(&2, &()), Some(&2)); + assert_eq!(tree.get(&4, &()), Some(&4)); } #[derive(Clone, Default, Debug)] @@ -889,7 +886,7 @@ mod tests { impl Summary for IntegersSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, other: &Self, _: &()) { self.count.0 += &other.count.0; self.sum.0 += &other.sum.0; self.contains_even |= other.contains_even; diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 2c16833395ce8ebb2c1c8a1b48454023afd31406..787d9fd404971fbee8c8b8692a7d27f4f131c214 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -249,9 +249,10 @@ impl Snapshot { #[cfg(test)] pub fn paths(&self) -> impl Iterator> { - let mut cursor = self.entries.cursor::<(), ()>(); - cursor.next(); - cursor.map(|entry| entry.path()) + self.entries + .cursor::<(), ()>() + .skip(1) + .map(|entry| entry.path()) } pub fn visible_files(&self, start: usize) -> FileIter { @@ -274,7 +275,7 @@ impl Snapshot { fn entry_for_path(&self, path: impl AsRef) -> Option<&Entry> { let mut cursor = self.entries.cursor::<_, ()>(); - if cursor.seek(&PathSearch::Exact(path.as_ref()), SeekBias::Left) { + if cursor.seek(&PathSearch::Exact(path.as_ref()), SeekBias::Left, &()) { cursor.item() } else { None @@ -296,7 +297,7 @@ impl Snapshot { self.ignores .insert(ignore_dir_path.into(), (Arc::new(ignore), self.scan_id)); } - self.entries.insert(entry); + self.entries.insert(entry, &()); } fn populate_dir( @@ -309,7 +310,7 @@ impl Snapshot { let mut parent_entry = self .entries - .get(&PathKey(parent_path.clone())) + .get(&PathKey(parent_path.clone()), &()) .unwrap() .clone(); if let Some(ignore) = ignore { @@ -325,15 +326,15 @@ impl Snapshot { for entry in entries { edits.push(Edit::Insert(entry)); } - self.entries.edit(edits); + self.entries.edit(edits, &()); } fn remove_path(&mut self, path: &Path) { let new_entries = { let mut cursor = self.entries.cursor::<_, ()>(); - let mut new_entries = cursor.slice(&PathSearch::Exact(path), SeekBias::Left); - cursor.seek_forward(&PathSearch::Successor(path), SeekBias::Left); - new_entries.push_tree(cursor.suffix()); + let mut new_entries = cursor.slice(&PathSearch::Exact(path), SeekBias::Left, &()); + cursor.seek_forward(&PathSearch::Successor(path), SeekBias::Left, &()); + new_entries.push_tree(cursor.suffix(&()), &()); new_entries }; self.entries = new_entries; @@ -538,7 +539,7 @@ impl Default for EntrySummary { impl sum_tree::Summary for EntrySummary { type Context = (); - fn add_summary(&mut self, rhs: &Self, _: Option<&Self::Context>) { + fn add_summary(&mut self, rhs: &Self, _: &()) { self.max_path = rhs.max_path.clone(); self.file_count += rhs.file_count; self.visible_file_count += rhs.visible_file_count; @@ -1068,7 +1069,7 @@ impl BackgroundScanner { edits.push(Edit::Insert(entry)); } } - self.snapshot.lock().entries.edit(edits); + self.snapshot.lock().entries.edit(edits, &()); } fn fs_entry_for_path(&self, path: Arc, abs_path: &Path) -> Result> { @@ -1164,13 +1165,13 @@ pub enum FileIter<'a> { impl<'a> FileIter<'a> { fn all(snapshot: &'a Snapshot, start: usize) -> Self { let mut cursor = snapshot.entries.cursor(); - cursor.seek(&FileCount(start), SeekBias::Right); + cursor.seek(&FileCount(start), SeekBias::Right, &()); Self::All(cursor) } fn visible(snapshot: &'a Snapshot, start: usize) -> Self { let mut cursor = snapshot.entries.cursor(); - cursor.seek(&VisibleFileCount(start), SeekBias::Right); + cursor.seek(&VisibleFileCount(start), SeekBias::Right, &()); Self::Visible(cursor) } @@ -1178,11 +1179,11 @@ impl<'a> FileIter<'a> { match self { Self::All(cursor) => { let ix = *cursor.start(); - cursor.seek_forward(&FileCount(ix.0 + 1), SeekBias::Right); + cursor.seek_forward(&FileCount(ix.0 + 1), SeekBias::Right, &()); } Self::Visible(cursor) => { let ix = *cursor.start(); - cursor.seek_forward(&VisibleFileCount(ix.0 + 1), SeekBias::Right); + cursor.seek_forward(&VisibleFileCount(ix.0 + 1), SeekBias::Right, &()); } } } @@ -1216,7 +1217,7 @@ struct ChildEntriesIter<'a> { impl<'a> ChildEntriesIter<'a> { fn new(parent_path: &'a Path, snapshot: &'a Snapshot) -> Self { let mut cursor = snapshot.entries.cursor(); - cursor.seek(&PathSearch::Exact(parent_path), SeekBias::Right); + cursor.seek(&PathSearch::Exact(parent_path), SeekBias::Right, &()); Self { parent_path, cursor, @@ -1231,7 +1232,7 @@ impl<'a> Iterator for ChildEntriesIter<'a> { if let Some(item) = self.cursor.item() { if item.path().starts_with(self.parent_path) { self.cursor - .seek_forward(&PathSearch::Successor(item.path()), SeekBias::Left); + .seek_forward(&PathSearch::Successor(item.path()), SeekBias::Left, &()); Some(item) } else { None From 2c1e4cae1931bc07f20eac11f77d961d97b9dc86 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 19:36:19 +0200 Subject: [PATCH 12/27] Merge `FoldRange` into `Fold` Co-Authored-By: Nathan Sobo --- zed/src/editor/display_map/fold_map.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 910f33558269f3c91499636d81ff49c845a770df..c02ac3c2368028dc29a43ec82fe21ebf5e055d17 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -109,10 +109,7 @@ impl FoldMap { self.folds = { let mut new_tree = SumTree::new(); let mut cursor = self.folds.cursor::<_, ()>(); - new_tree.push_tree( - cursor.slice(&FoldRange(fold.0.clone()), SeekBias::Right, buffer), - buffer, - ); + new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer); new_tree.push(fold, buffer); new_tree.push_tree(cursor.suffix(buffer), buffer); new_tree @@ -298,7 +295,7 @@ impl FoldMap { let anchor = buffer.anchor_before(edit.new_range.start).unwrap(); let mut folds_cursor = self.folds.cursor::<_, ()>(); - folds_cursor.seek(&FoldRange(anchor..Anchor::End), SeekBias::Left, buffer); + folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer); let mut folds = folds_cursor .map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap()) .peekable(); @@ -482,6 +479,12 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary { #[derive(Clone, Debug)] struct Fold(Range); +impl Default for Fold { + fn default() -> Self { + Self(Anchor::Start..Anchor::End) + } +} + impl sum_tree::Item for Fold { type Summary = FoldSummary; @@ -542,23 +545,14 @@ impl sum_tree::Summary for FoldSummary { } } -#[derive(Clone, Debug)] -struct FoldRange(Range); - -impl Default for FoldRange { - fn default() -> Self { - Self(Anchor::Start..Anchor::End) - } -} - -impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange { +impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold { fn add_summary(&mut self, summary: &'a FoldSummary) { self.0.start = summary.start.clone(); self.0.end = summary.end.clone(); } } -impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for FoldRange { +impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold { fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering { self.0.cmp(&other.0, buffer).unwrap() } From 6890a3d9faae106ca86d592dee336fe8d6ec8a97 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 May 2021 19:50:51 +0200 Subject: [PATCH 13/27] Batch fold insertion Co-Authored-By: Nathan Sobo --- zed/src/editor/display_map/fold_map.rs | 37 +++++++++++++++----------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index c02ac3c2368028dc29a43ec82fe21ebf5e055d17..f459c9f812b61331eacd47cd73012f7590607e25 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -94,27 +94,22 @@ impl FoldMap { let _ = self.sync(ctx); let mut edits = Vec::new(); + let mut folds = Vec::new(); let buffer = self.buffer.read(ctx); for range in ranges.into_iter() { let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?; - if range.start == range.end { - continue; + if range.start != range.end { + let fold = + Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?); + folds.push(fold); + edits.push(Edit { + old_range: range.clone(), + new_range: range.clone(), + }); } - - let fold = Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?); - edits.push(Edit { - old_range: range.clone(), - new_range: range.clone(), - }); - self.folds = { - let mut new_tree = SumTree::new(); - let mut cursor = self.folds.cursor::<_, ()>(); - new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer); - new_tree.push(fold, buffer); - new_tree.push_tree(cursor.suffix(buffer), buffer); - new_tree - }; } + + folds.sort_unstable_by(|a, b| sum_tree::SeekDimension::cmp(a, b, buffer)); edits.sort_unstable_by(|a, b| { a.old_range .start @@ -122,6 +117,16 @@ impl FoldMap { .then_with(|| b.old_range.end.cmp(&a.old_range.end)) }); + self.folds = { + let mut new_tree = SumTree::new(); + let mut cursor = self.folds.cursor::<_, ()>(); + for fold in folds { + new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer); + new_tree.push(fold, buffer); + } + new_tree.push_tree(cursor.suffix(buffer), buffer); + new_tree + }; self.apply_edits(edits, ctx); Ok(()) } From 290fcb4c06ac19c8014f3df69567cde7775bdbc0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 May 2021 13:18:41 -0700 Subject: [PATCH 14/27] In handle ::condition, re-poll on events as well as notifications --- gpui/src/app.rs | 147 +++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 63 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 798805f63ebb98923956d5ea73b2b558706e3545..c6eaa5df9a95d5260e6b7931492ba44c644b4aeb 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -12,12 +12,12 @@ use keymap::MatchResult; use parking_lot::{Mutex, RwLock}; use pathfinder_geometry::{rect::RectF, vector::vec2f}; use platform::Event; -use postage::{sink::Sink as _, stream::Stream as _}; +use postage::{mpsc, sink::Sink as _, stream::Stream as _}; use smol::prelude::*; use std::{ any::{type_name, Any, TypeId}, cell::RefCell, - collections::{hash_map::Entry, HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, fmt::{self, Debug}, hash::{Hash, Hasher}, marker::PhantomData, @@ -388,7 +388,6 @@ pub struct MutableAppContext { subscriptions: HashMap>, model_observations: HashMap>, view_observations: HashMap>, - async_observations: HashMap>, window_invalidations: HashMap, presenters_and_platform_windows: HashMap>, Box)>, @@ -430,7 +429,6 @@ impl MutableAppContext { subscriptions: HashMap::new(), model_observations: HashMap::new(), view_observations: HashMap::new(), - async_observations: HashMap::new(), window_invalidations: HashMap::new(), presenters_and_platform_windows: HashMap::new(), debug_elements_callbacks: HashMap::new(), @@ -897,13 +895,11 @@ impl MutableAppContext { self.ctx.models.remove(&model_id); self.subscriptions.remove(&model_id); self.model_observations.remove(&model_id); - self.async_observations.remove(&model_id); } for (window_id, view_id) in dropped_views { self.subscriptions.remove(&view_id); self.model_observations.remove(&view_id); - self.async_observations.remove(&view_id); if let Some(window) = self.ctx.windows.get_mut(&window_id) { self.window_invalidations .entry(window_id) @@ -1082,12 +1078,6 @@ impl MutableAppContext { } } } - - if let Entry::Occupied(mut entry) = self.async_observations.entry(observed_id) { - if entry.get_mut().blocking_send(()).is_err() { - entry.remove_entry(); - } - } } fn notify_view_observers(&mut self, window_id: usize, view_id: usize) { @@ -1098,7 +1088,12 @@ impl MutableAppContext { .insert(view_id); if let Some(observations) = self.view_observations.remove(&view_id) { - if self.ctx.models.contains_key(&view_id) { + if self + .ctx + .windows + .get(&window_id) + .map_or(false, |w| w.views.contains_key(&view_id)) + { for mut observation in observations { let alive = if let Some(mut view) = self .ctx @@ -1134,12 +1129,6 @@ impl MutableAppContext { } } } - - if let Entry::Occupied(mut entry) = self.async_observations.entry(view_id) { - if entry.get_mut().blocking_send(()).is_err() { - entry.remove_entry(); - } - } } fn focus(&mut self, window_id: usize, focused_id: usize) { @@ -1780,6 +1769,10 @@ impl<'a, T: View> ViewContext<'a, T> { self.window_id } + pub fn view_id(&self) -> usize { + self.view_id + } + pub fn foreground(&self) -> &Rc { self.app.foreground_executor() } @@ -1855,22 +1848,11 @@ impl<'a, T: View> ViewContext<'a, T> { F: 'static + FnMut(&mut T, ModelHandle, &E::Event, &mut ViewContext), { let emitter_handle = handle.downgrade(); - self.app - .subscriptions - .entry(handle.id()) - .or_default() - .push(Subscription::FromView { - window_id: self.window_id, - view_id: self.view_id, - callback: Box::new(move |view, payload, app, window_id, view_id| { - if let Some(emitter_handle) = emitter_handle.upgrade(app.as_ref()) { - let model = view.downcast_mut().expect("downcast is type safe"); - let payload = payload.downcast_ref().expect("downcast is type safe"); - let mut ctx = ViewContext::new(app, window_id, view_id); - callback(model, emitter_handle, payload, &mut ctx); - } - }), - }); + self.subscribe(handle, move |model, payload, ctx| { + if let Some(emitter_handle) = emitter_handle.upgrade(ctx.as_ref()) { + callback(model, emitter_handle, payload, ctx); + } + }); } pub fn subscribe_to_view(&mut self, handle: &ViewHandle, mut callback: F) @@ -1880,7 +1862,19 @@ impl<'a, T: View> ViewContext<'a, T> { F: 'static + FnMut(&mut T, ViewHandle, &V::Event, &mut ViewContext), { let emitter_handle = handle.downgrade(); + self.subscribe(handle, move |view, payload, ctx| { + if let Some(emitter_handle) = emitter_handle.upgrade(ctx.as_ref()) { + callback(view, emitter_handle, payload, ctx); + } + }); + } + pub fn subscribe(&mut self, handle: &impl Handle, mut callback: F) + where + E: Entity, + E::Event: 'static, + F: 'static + FnMut(&mut T, &E::Event, &mut ViewContext), + { self.app .subscriptions .entry(handle.id()) @@ -1888,13 +1882,11 @@ impl<'a, T: View> ViewContext<'a, T> { .push(Subscription::FromView { window_id: self.window_id, view_id: self.view_id, - callback: Box::new(move |view, payload, app, window_id, view_id| { - if let Some(emitter_handle) = emitter_handle.upgrade(&app) { - let model = view.downcast_mut().expect("downcast is type safe"); - let payload = payload.downcast_ref().expect("downcast is type safe"); - let mut ctx = ViewContext::new(app, window_id, view_id); - callback(model, emitter_handle, payload, &mut ctx); - } + callback: Box::new(move |entity, payload, app, window_id, view_id| { + let entity = entity.downcast_mut().expect("downcast is type safe"); + let payload = payload.downcast_ref().expect("downcast is type safe"); + let mut ctx = ViewContext::new(app, window_id, view_id); + callback(entity, payload, &mut ctx); }), }); } @@ -2138,12 +2130,24 @@ impl ModelHandle { ctx: &TestAppContext, mut predicate: impl FnMut(&T, &AppContext) -> bool, ) -> impl Future { + let (tx, mut rx) = mpsc::channel(1024); + let mut ctx = ctx.0.borrow_mut(); - let tx = ctx - .async_observations - .entry(self.id()) - .or_insert_with(|| postage::broadcast::channel(128).0); - let mut rx = tx.subscribe(); + self.update(&mut *ctx, |_, ctx| { + ctx.observe(self, { + let mut tx = tx.clone(); + move |_, _, _| { + tx.blocking_send(()).ok(); + } + }); + ctx.subscribe(self, { + let mut tx = tx.clone(); + move |_, _, _| { + tx.blocking_send(()).ok(); + } + }) + }); + let ctx = ctx.weak_self.as_ref().unwrap().upgrade().unwrap(); let handle = self.downgrade(); @@ -2310,19 +2314,41 @@ impl ViewHandle { pub fn condition( &self, ctx: &TestAppContext, - mut predicate: impl 'static + FnMut(&T, &AppContext) -> bool, - ) -> impl 'static + Future { + predicate: impl FnMut(&T, &AppContext) -> bool, + ) -> impl Future { + self.condition_with_duration(Duration::from_millis(500), ctx, predicate) + } + + pub fn condition_with_duration( + &self, + duration: Duration, + ctx: &TestAppContext, + mut predicate: impl FnMut(&T, &AppContext) -> bool, + ) -> impl Future { + let (tx, mut rx) = mpsc::channel(1024); + let mut ctx = ctx.0.borrow_mut(); - let tx = ctx - .async_observations - .entry(self.id()) - .or_insert_with(|| postage::broadcast::channel(128).0); - let mut rx = tx.subscribe(); + self.update(&mut *ctx, |_, ctx| { + ctx.observe_view(self, { + let mut tx = tx.clone(); + move |_, _, _| { + tx.blocking_send(()).ok(); + } + }); + + ctx.subscribe(self, { + let mut tx = tx.clone(); + move |_, _, _| { + tx.blocking_send(()).ok(); + } + }) + }); + let ctx = ctx.weak_self.as_ref().unwrap().upgrade().unwrap(); let handle = self.downgrade(); async move { - timeout(Duration::from_millis(200), async move { + timeout(duration, async move { loop { { let ctx = ctx.borrow(); @@ -2330,7 +2356,7 @@ impl ViewHandle { if predicate( handle .upgrade(ctx) - .expect("model dropped with pending condition") + .expect("view dropped with pending condition") .read(ctx), ctx, ) { @@ -2340,7 +2366,7 @@ impl ViewHandle { rx.recv() .await - .expect("model dropped with pending condition"); + .expect("view dropped with pending condition"); } }) .await @@ -3537,9 +3563,7 @@ mod tests { model.update(&mut app, |model, ctx| model.inc(ctx)); assert_eq!(poll_once(&mut condition2).await, Some(())); - // Broadcast channel should be removed if no conditions remain on next notification. model.update(&mut app, |_, ctx| ctx.notify()); - app.update(|ctx| assert!(ctx.async_observations.get(&model.id()).is_none())); }); } @@ -3617,10 +3641,7 @@ mod tests { view.update(&mut app, |view, ctx| view.inc(ctx)); assert_eq!(poll_once(&mut condition2).await, Some(())); - - // Broadcast channel should be removed if no conditions remain on next notification. view.update(&mut app, |_, ctx| ctx.notify()); - app.update(|ctx| assert!(ctx.async_observations.get(&view.id()).is_none())); }); } @@ -3650,7 +3671,7 @@ mod tests { } #[test] - #[should_panic(expected = "model dropped with pending condition")] + #[should_panic(expected = "view dropped with pending condition")] fn test_view_condition_panic_on_drop() { struct View; From e6323f0d02ef89e5d3bfff36d4c492ca07694f44 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 May 2021 21:06:20 -0700 Subject: [PATCH 15/27] Fix handling of new files with new buffer/file structure --- gpui/src/app.rs | 4 ++ gpui/src/platform/test.rs | 4 ++ zed/src/editor/buffer/mod.rs | 4 +- zed/src/workspace.rs | 115 ++++++++++++----------------------- 4 files changed, 50 insertions(+), 77 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index c6eaa5df9a95d5260e6b7931492ba44c644b4aeb..cc844581ae71c04aea3b5a756e487b6c312d59ac 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -339,6 +339,10 @@ impl TestAppContext { pub fn simulate_new_path_selection(&self, result: impl FnOnce(PathBuf) -> Option) { self.1.as_ref().simulate_new_path_selection(result); } + + pub fn did_prompt_for_new_path(&self) -> bool { + self.1.as_ref().did_prompt_for_new_path() + } } impl UpdateModel for TestAppContext { diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 6ee772c7e05467911dda82ef705ad512ada5b1da..c0e1057409cb8474d8c59be018f402a9bd9e7b50 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -45,6 +45,10 @@ impl Platform { .expect("prompt_for_new_path was not called"); callback(result(dir_path)); } + + pub(crate) fn did_prompt_for_new_path(&self) -> bool { + self.last_prompt_for_new_path_args.borrow().is_some() + } } impl super::Platform for Platform { diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 0c96ac0bff514401d8f73d4e7c3a674f5df1972f..72ead904d26a3dc703417f319ddbde3f901d461d 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -484,7 +484,9 @@ impl Buffer { file: Option, ctx: &mut ModelContext, ) { - self.file = file; + if file.is_some() { + self.file = file; + } self.saved_version = version; ctx.emit(Event::Saved); } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 1d1cfaf0476908b05fd66de0bf325f674f141b40..12888237e3919e7f800ef093692cf30d17f85731 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -714,7 +714,8 @@ mod tests { use crate::{editor::BufferView, settings, test::temp_tree}; use gpui::App; use serde_json::json; - use std::{collections::HashSet, os::unix}; + use std::collections::HashSet; + use std::time; use tempdir::TempDir; #[test] @@ -957,75 +958,23 @@ mod tests { } #[test] - fn test_open_two_paths_to_the_same_file() { - use crate::workspace::ItemViewHandle; - + fn test_open_and_save_new_file() { App::test_async((), |mut app| async move { - // Create a worktree with a symlink: - // dir - // ├── hello.txt - // └── hola.txt -> hello.txt - let temp_dir = temp_tree(json!({ "hello.txt": "hi" })); - let dir = temp_dir.path(); - unix::fs::symlink(dir.join("hello.txt"), dir.join("hola.txt")).unwrap(); - + let dir = TempDir::new("test-new-file").unwrap(); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, workspace) = app.add_window(|ctx| { let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(dir, ctx); + workspace.add_worktree(dir.path(), ctx); workspace }); - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - - // Simultaneously open both the original file and the symlink to the same file. - app.update(|ctx| { - workspace.update(ctx, |view, ctx| { - view.open_paths(&[dir.join("hello.txt"), dir.join("hola.txt")], ctx) - }) - }) - .await; - - // The same content shows up with two different editors. - let buffer_views = app.read(|ctx| { + let worktree = app.read(|ctx| { workspace .read(ctx) - .active_pane() - .read(ctx) - .items() + .worktrees() .iter() - .map(|i| i.to_any().downcast::().unwrap()) - .collect::>() - }); - app.read(|ctx| { - assert_eq!(buffer_views[0].title(ctx), "hello.txt"); - assert_eq!(buffer_views[1].title(ctx), "hola.txt"); - assert_eq!(buffer_views[0].read(ctx).text(ctx), "hi"); - assert_eq!(buffer_views[1].read(ctx).text(ctx), "hi"); - }); - - // When modifying one buffer, the changes appear in both editors. - app.update(|ctx| { - buffer_views[0].update(ctx, |buf, ctx| { - buf.insert(&"oh, ".to_string(), ctx); - }); - }); - app.read(|ctx| { - assert_eq!(buffer_views[0].read(ctx).text(ctx), "oh, hi"); - assert_eq!(buffer_views[1].read(ctx).text(ctx), "oh, hi"); - }); - }); - } - - #[test] - fn test_open_and_save_new_file() { - App::test_async((), |mut app| async move { - let dir = TempDir::new("test-new-file").unwrap(); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(dir.path(), ctx); - workspace + .next() + .unwrap() + .clone() }); // Create a new untitled buffer @@ -1039,11 +988,13 @@ mod tests { .unwrap() }); editor.update(&mut app, |editor, ctx| { + assert!(!editor.is_dirty(ctx.as_ref())); assert_eq!(editor.title(ctx.as_ref()), "untitled"); - editor.insert(&"hi".to_string(), ctx) + editor.insert(&"hi".to_string(), ctx); + assert!(editor.is_dirty(ctx.as_ref())); }); - // Save the buffer, selecting a filename + // Save the buffer. This prompts for a filename. workspace.update(&mut app, |workspace, ctx| { workspace.save_active_item(&(), ctx) }); @@ -1051,26 +1002,38 @@ mod tests { assert_eq!(parent_dir, dir.path()); Some(parent_dir.join("the-new-name")) }); - app.read(|ctx| assert_eq!(editor.title(ctx), "untitled")); + app.read(|ctx| { + assert!(editor.is_dirty(ctx)); + assert_eq!(editor.title(ctx), "untitled"); + }); // When the save completes, the buffer's title is updated. - let worktree = app.read(|ctx| { - workspace - .read(ctx) - .worktrees() - .iter() - .next() - .unwrap() - .clone() - }); + editor + .condition(&app, |editor, ctx| !editor.is_dirty(ctx)) + .await; worktree - .condition(&app, |worktree, _| { + .condition_with_duration(time::Duration::from_millis(500), &app, |worktree, _| { worktree.inode_for_path("the-new-name").is_some() }) .await; + app.read(|ctx| assert_eq!(editor.title(ctx), "the-new-name")); + + // Edit the file and save it again. This time, there is no filename prompt. + editor.update(&mut app, |editor, ctx| { + editor.insert(&" there".to_string(), ctx); + assert_eq!(editor.is_dirty(ctx.as_ref()), true); + }); + workspace.update(&mut app, |workspace, ctx| { + workspace.save_active_item(&(), ctx) + }); + assert!(!app.did_prompt_for_new_path()); + editor + .condition(&app, |editor, ctx| !editor.is_dirty(ctx)) + .await; + app.read(|ctx| assert_eq!(editor.title(ctx), "the-new-name")); - // Open the same newly-created file in another pane item. - // The new editor should reuse the same buffer. + // Open the same newly-created file in another pane item. The new editor should reuse + // the same buffer. workspace.update(&mut app, |workspace, ctx| { workspace.open_new_file(&(), ctx); workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, ctx); From 6a2fe38d94fdbf6b512ca876cc9131a01e18eb9d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 09:39:55 +0200 Subject: [PATCH 16/27] Verify `FoldMap::unfold` in randomized test --- zed/src/editor/display_map/fold_map.rs | 60 +++++++++++++++++--------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index f459c9f812b61331eacd47cd73012f7590607e25..c64d48d12d930d7d05f0a5bf0b424006801911aa 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -143,7 +143,7 @@ impl FoldMap { let mut edits = Vec::new(); let mut fold_ixs_to_delete = Vec::new(); for range in ranges.into_iter() { - // Remove intersecting folds and add their ranges to edits that are passed to apply_edits + // Remove intersecting folds and add their ranges to edits that are passed to apply_edits. let mut folds_cursor = self.intersecting_folds(range, ctx)?; while let Some(fold) = folds_cursor.item() { let offset_range = @@ -156,8 +156,15 @@ impl FoldMap { folds_cursor.next(); } } + fold_ixs_to_delete.sort_unstable(); fold_ixs_to_delete.dedup(); + edits.sort_unstable_by(|a, b| { + a.old_range + .start + .cmp(&b.old_range.start) + .then_with(|| b.old_range.end.cmp(&a.old_range.end)) + }); self.folds = { let mut cursor = self.folds.cursor::<_, ()>(); @@ -169,7 +176,6 @@ impl FoldMap { folds.push_tree(cursor.suffix(buffer), buffer); folds }; - self.apply_edits(edits, ctx); Ok(()) } @@ -868,26 +874,38 @@ mod tests { for _ in 0..operations { log::info!("text: {:?}", buffer.read(app).text()); - if rng.gen() { - let buffer = buffer.read(app); - - let fold_count = rng.gen_range(1..=5); - let mut fold_ranges: Vec> = Vec::new(); - for _ in 0..fold_count { - let end = rng.gen_range(0..buffer.len() + 1); - let start = rng.gen_range(0..end + 1); - fold_ranges.push(start..end); + match rng.gen_range(0..=100) { + 0..=34 => { + let buffer = buffer.read(app); + let mut to_fold = Vec::new(); + for _ in 0..rng.gen_range(1..=5) { + let end = rng.gen_range(0..=buffer.len()); + let start = rng.gen_range(0..=end); + to_fold.push(start..end); + } + log::info!("folding {:?}", to_fold); + map.fold(to_fold, app.as_ref()).unwrap(); + } + 35..=59 if !map.folds.is_empty() => { + let buffer = buffer.read(app); + let mut to_unfold = Vec::new(); + for _ in 0..rng.gen_range(1..=3) { + let end = rng.gen_range(0..=buffer.len()); + let start = rng.gen_range(0..=end); + to_unfold.push(start..end); + } + log::info!("unfolding {:?}", to_unfold); + map.unfold(to_unfold, app.as_ref()).unwrap(); + } + _ => { + let edits = buffer.update(app, |buffer, ctx| { + let start_version = buffer.version.clone(); + let edit_count = rng.gen_range(1..=5); + buffer.randomly_edit(&mut rng, edit_count, Some(ctx)); + buffer.edits_since(start_version).collect::>() + }); + log::info!("editing {:?}", edits); } - log::info!("folding {:?}", fold_ranges); - map.fold(fold_ranges.clone(), app.as_ref()).unwrap(); - } else { - let edits = buffer.update(app, |buffer, ctx| { - let start_version = buffer.version.clone(); - let edit_count = rng.gen_range(1..=5); - buffer.randomly_edit(&mut rng, edit_count, Some(ctx)); - buffer.edits_since(start_version).collect::>() - }); - log::info!("editing {:?}", edits); } map.check_invariants(app.as_ref()); From 06fb4e8200ab5cb91ad2c40da1d1470daf08de9e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 11:13:21 +0200 Subject: [PATCH 17/27] Implement `select_line` for buffer --- zed/src/editor/buffer/selection.rs | 4 +- zed/src/editor/buffer_view.rs | 81 +++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/zed/src/editor/buffer/selection.rs b/zed/src/editor/buffer/selection.rs index 431f8224c425d6e568da07718af36827520f7e64..dd8ea1ea2f7031481afef4a2525e0d78adfaaffb 100644 --- a/zed/src/editor/buffer/selection.rs +++ b/zed/src/editor/buffer/selection.rs @@ -75,6 +75,7 @@ impl Selection { pub fn buffer_rows_for_display_rows( &self, + expand_if_ends_at_line_start: bool, map: &DisplayMap, ctx: &AppContext, ) -> (Range, Range) { @@ -84,7 +85,8 @@ impl Selection { .unwrap(); let mut display_end = self.end.to_display_point(map, ctx).unwrap(); - if display_end.row() != map.max_point(ctx).row() + if !expand_if_ends_at_line_start + && display_end.row() != map.max_point(ctx).row() && display_start.row() != display_end.row() && display_end.column() == 0 { diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 7d80c68217ac454d22684f582f4ab6b71ec3140d..9bf964a9f0d4facfe6d9e08008c1cd7da1ed1705 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -139,6 +139,7 @@ pub fn init(app: &mut MutableAppContext) { ), Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")), Binding::new("cmd-a", "buffer:select_all", Some("BufferView")), + Binding::new("cmd-l", "buffer:select_line", Some("BufferView")), Binding::new("pageup", "buffer:page_up", Some("BufferView")), Binding::new("pagedown", "buffer:page_down", Some("BufferView")), Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")), @@ -229,6 +230,7 @@ pub fn init(app: &mut MutableAppContext) { ); app.add_action("buffer:select_to_end", BufferView::select_to_end); app.add_action("buffer:select_all", BufferView::select_all); + app.add_action("buffer:select_line", BufferView::select_line); app.add_action("buffer:page_up", BufferView::page_up); app.add_action("buffer:page_down", BufferView::page_down); app.add_action("buffer:fold", BufferView::fold); @@ -712,7 +714,8 @@ impl BufferView { let mut selections = self.selections(app).iter().peekable(); while let Some(selection) = selections.next() { - let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app); + let (mut rows, _) = + selection.buffer_rows_for_display_rows(false, &self.display_map, app); let goal_display_column = selection .head() .to_display_point(&self.display_map, app) @@ -722,7 +725,7 @@ impl BufferView { // Accumulate contiguous regions of rows that we want to delete. while let Some(next_selection) = selections.peek() { let (next_rows, _) = - next_selection.buffer_rows_for_display_rows(&self.display_map, app); + next_selection.buffer_rows_for_display_rows(false, &self.display_map, app); if next_rows.start <= rows.end { rows.end = next_rows.end; selections.next().unwrap(); @@ -803,10 +806,11 @@ impl BufferView { let mut selections_iter = selections.iter_mut().peekable(); while let Some(selection) = selections_iter.next() { // Avoid duplicating the same lines twice. - let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app); + let (mut rows, _) = + selection.buffer_rows_for_display_rows(false, &self.display_map, app); while let Some(next_selection) = selections_iter.peek() { let (next_rows, _) = - next_selection.buffer_rows_for_display_rows(&self.display_map, app); + next_selection.buffer_rows_for_display_rows(false, &self.display_map, app); if next_rows.start <= rows.end - 1 { rows.end = next_rows.end; selections_iter.next().unwrap(); @@ -860,10 +864,10 @@ impl BufferView { // Accumulate contiguous regions of rows that we want to move. contiguous_selections.push(selection.range(buffer)); let (mut buffer_rows, mut display_rows) = - selection.buffer_rows_for_display_rows(&self.display_map, app); + selection.buffer_rows_for_display_rows(false, &self.display_map, app); while let Some(next_selection) = selections.peek() { let (next_buffer_rows, next_display_rows) = - next_selection.buffer_rows_for_display_rows(&self.display_map, app); + next_selection.buffer_rows_for_display_rows(false, &self.display_map, app); if next_buffer_rows.start <= buffer_rows.end { buffer_rows.end = next_buffer_rows.end; display_rows.end = next_display_rows.end; @@ -950,10 +954,10 @@ impl BufferView { // Accumulate contiguous regions of rows that we want to move. contiguous_selections.push(selection.range(buffer)); let (mut buffer_rows, mut display_rows) = - selection.buffer_rows_for_display_rows(&self.display_map, app); + selection.buffer_rows_for_display_rows(false, &self.display_map, app); while let Some(next_selection) = selections.peek() { let (next_buffer_rows, next_display_rows) = - next_selection.buffer_rows_for_display_rows(&self.display_map, app); + next_selection.buffer_rows_for_display_rows(false, &self.display_map, app); if next_buffer_rows.start <= buffer_rows.end { buffer_rows.end = next_buffer_rows.end; display_rows.end = next_display_rows.end; @@ -1662,6 +1666,22 @@ impl BufferView { self.update_selections(vec![selection], false, ctx); } + pub fn select_line(&mut self, _: &(), ctx: &mut ViewContext) { + let app = ctx.as_ref(); + let buffer = self.buffer.read(app); + let mut selections = self.selections(app).to_vec(); + let max_point = buffer.max_point(); + for selection in &mut selections { + let (rows, _) = selection.buffer_rows_for_display_rows(true, &self.display_map, app); + selection.start = buffer.anchor_before(Point::new(rows.start, 0)).unwrap(); + selection.end = buffer + .anchor_before(cmp::min(max_point, Point::new(rows.end, 0))) + .unwrap(); + selection.reversed = false; + } + self.update_selections(selections, true, ctx); + } + pub fn selections_in_range<'a>( &'a self, range: Range, @@ -3284,6 +3304,51 @@ mod tests { }); } + #[test] + fn test_select_line() { + App::test((), |app| { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 5))); + let (_, view) = + app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), + ], + ctx, + ) + .unwrap(); + view.select_line(&(), ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0), + DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0), + ] + ); + + view.update(app, |view, ctx| view.select_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0), + DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5), + ] + ); + + view.update(app, |view, ctx| view.select_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)] + ); + }); + } + impl BufferView { fn selection_ranges(&self, app: &AppContext) -> Vec> { self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app) From 1c2f549c82af371a872059b2211083f61166c68c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 14:30:40 +0200 Subject: [PATCH 18/27] Don't report folds that touch the endpoints of the given range Fold ranges are exclusive, exactly like the query ranges. So if the end of a fold coincides with the start of the query range, we shouldn't report that fold. Analogously, if the start of a fold coincides with the end of the query range, that fold shouldn't be reported. --- zed/src/editor/display_map/fold_map.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index c64d48d12d930d7d05f0a5bf0b424006801911aa..a14a0a5bbb26b092b680800c62a01f1833023cbc 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -192,8 +192,8 @@ impl FoldMap { let start = buffer.anchor_before(range.start.to_offset(buffer)?)?; let end = buffer.anchor_after(range.end.to_offset(buffer)?)?; Ok(self.folds.filter::<_, usize>(move |summary| { - start.cmp(&summary.max_end, buffer).unwrap() <= Ordering::Equal - && end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal + start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less + && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater })) } @@ -712,7 +712,7 @@ mod tests { }); assert_eq!(map.text(app.as_ref()), "123a…c123456eee"); - map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref()) + map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref()) .unwrap(); assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee"); }); @@ -833,7 +833,6 @@ mod tests { fold_ranges, vec![ Point::new(0, 2)..Point::new(2, 2), - Point::new(0, 4)..Point::new(1, 0), Point::new(1, 2)..Point::new(3, 2) ] ); @@ -1007,9 +1006,10 @@ mod tests { .items() .into_iter() .filter(|fold| { - let fold_start = fold.0.start.to_offset(buffer).unwrap(); - let fold_end = fold.0.end.to_offset(buffer).unwrap(); - start <= fold_end && end >= fold_start + let start = buffer.anchor_before(start).unwrap(); + let end = buffer.anchor_after(end).unwrap(); + start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less + && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater }) .map(|fold| fold.0) .collect::>(); From 4797ccfdcd7bec9ae157e342861c9afcd99bb8fc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 14:32:57 +0200 Subject: [PATCH 19/27] Implement `split_selection_into_lines` for buffer --- zed/src/editor/buffer_view.rs | 123 ++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 9bf964a9f0d4facfe6d9e08008c1cd7da1ed1705..877858a36f4d7c0508ec159ee1b0d7cd639cbab3 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -140,6 +140,11 @@ pub fn init(app: &mut MutableAppContext) { Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")), Binding::new("cmd-a", "buffer:select_all", Some("BufferView")), Binding::new("cmd-l", "buffer:select_line", Some("BufferView")), + Binding::new( + "cmd-shift-L", + "buffer:split_selection_into_lines", + Some("BufferView"), + ), Binding::new("pageup", "buffer:page_up", Some("BufferView")), Binding::new("pagedown", "buffer:page_down", Some("BufferView")), Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")), @@ -231,6 +236,10 @@ pub fn init(app: &mut MutableAppContext) { app.add_action("buffer:select_to_end", BufferView::select_to_end); app.add_action("buffer:select_all", BufferView::select_all); app.add_action("buffer:select_line", BufferView::select_line); + app.add_action( + "buffer:split_selection_into_lines", + BufferView::split_selection_into_lines, + ); app.add_action("buffer:page_up", BufferView::page_up); app.add_action("buffer:page_down", BufferView::page_down); app.add_action("buffer:fold", BufferView::fold); @@ -1682,6 +1691,45 @@ impl BufferView { self.update_selections(selections, true, ctx); } + pub fn split_selection_into_lines(&mut self, _: &(), ctx: &mut ViewContext) { + let app = ctx.as_ref(); + let buffer = self.buffer.read(app); + + let mut to_unfold = Vec::new(); + let mut new_selections = Vec::new(); + for selection in self.selections(app) { + let range = selection.range(buffer); + if range.start.row != range.end.row { + new_selections.push(Selection { + start: selection.start.clone(), + end: selection.start.clone(), + reversed: false, + goal_column: None, + }); + } + for row in range.start.row + 1..range.end.row { + let cursor = buffer + .anchor_before(Point::new(row, buffer.line_len(row).unwrap())) + .unwrap(); + new_selections.push(Selection { + start: cursor.clone(), + end: cursor, + reversed: false, + goal_column: None, + }); + } + new_selections.push(Selection { + start: selection.end.clone(), + end: selection.end.clone(), + reversed: false, + goal_column: None, + }); + to_unfold.push(range); + } + self.unfold_ranges(to_unfold, ctx); + self.update_selections(new_selections, true, ctx); + } + pub fn selections_in_range<'a>( &'a self, range: Range, @@ -3349,6 +3397,81 @@ mod tests { }); } + #[test] + fn test_split_selection_into_lines() { + App::test((), |app| { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|_| Buffer::new(0, sample_text(9, 5))); + let (_, view) = + app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + view.update(app, |view, ctx| { + view.fold_ranges( + vec![ + Point::new(0, 2)..Point::new(1, 2), + Point::new(2, 3)..Point::new(4, 1), + Point::new(7, 0)..Point::new(8, 4), + ], + ctx, + ); + view.select_display_ranges( + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), + ], + ctx, + ) + .unwrap(); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nfffff\nggggg\n…i" + ); + + view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nfffff\nggggg\n…i" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2) + ] + ); + + view.update(app, |view, ctx| { + view.select_display_ranges( + &[DisplayPoint::new(0, 1)..DisplayPoint::new(4, 0)], + ctx, + ) + .unwrap(); + view.split_selection_into_lines(&(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), + DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), + DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5), + DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5), + DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5), + DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5), + DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0) + ] + ); + }); + } + impl BufferView { fn selection_ranges(&self, app: &AppContext) -> Vec> { self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app) From 5f1d0b8850bc0fe9db222cded2529d11b4c3a0ae Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 14:38:21 +0200 Subject: [PATCH 20/27] :lipstick: --- zed/src/editor/buffer/selection.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zed/src/editor/buffer/selection.rs b/zed/src/editor/buffer/selection.rs index dd8ea1ea2f7031481afef4a2525e0d78adfaaffb..8f436a04e7a4929c6a542c10e5df6d8086e0d10c 100644 --- a/zed/src/editor/buffer/selection.rs +++ b/zed/src/editor/buffer/selection.rs @@ -75,7 +75,7 @@ impl Selection { pub fn buffer_rows_for_display_rows( &self, - expand_if_ends_at_line_start: bool, + include_end_if_at_line_start: bool, map: &DisplayMap, ctx: &AppContext, ) -> (Range, Range) { @@ -85,7 +85,7 @@ impl Selection { .unwrap(); let mut display_end = self.end.to_display_point(map, ctx).unwrap(); - if !expand_if_ends_at_line_start + if !include_end_if_at_line_start && display_end.row() != map.max_point(ctx).row() && display_start.row() != display_end.row() && display_end.column() == 0 From f85669c681c1bdc846bd01fe9be1ef4892bcf461 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 14:42:56 +0200 Subject: [PATCH 21/27] Include debug symbols in release mode --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c58e56b67a4349d3dc7ada8b8019805ed603d37f..7dcf616a67dec6e21da7f96b35b1e87882cd0403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ core-graphics = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a [profile.dev] split-debuginfo = "unpacked" + +[profile.release] +debug = true From 8bbaa0bd34caee0548e645cc2ecd7153ffac7206 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 14:54:12 +0200 Subject: [PATCH 22/27] Handle reversed selections correctly in `split_selection_into_lines` --- zed/src/editor/buffer_view.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 877858a36f4d7c0508ec159ee1b0d7cd639cbab3..0cd948809183d29f0d6b2bfae4f785a7b4ac22bc 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1692,13 +1692,15 @@ impl BufferView { } pub fn split_selection_into_lines(&mut self, _: &(), ctx: &mut ViewContext) { + use super::RangeExt; + let app = ctx.as_ref(); let buffer = self.buffer.read(app); let mut to_unfold = Vec::new(); let mut new_selections = Vec::new(); for selection in self.selections(app) { - let range = selection.range(buffer); + let range = selection.range(buffer).sorted(); if range.start.row != range.end.row { new_selections.push(Selection { start: selection.start.clone(), @@ -3446,7 +3448,7 @@ mod tests { view.update(app, |view, ctx| { view.select_display_ranges( - &[DisplayPoint::new(0, 1)..DisplayPoint::new(4, 0)], + &[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)], ctx, ) .unwrap(); From 806c09bf11f9c6f3c93b75cea04d07f852c72a13 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 May 2021 18:15:00 +0200 Subject: [PATCH 23/27] Autoscroll when folding an arbitrary range --- zed/src/editor/buffer_view.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 0cd948809183d29f0d6b2bfae4f785a7b4ac22bc..18e7a4d0c00ea78afe1850b149824d05190039d7 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1952,9 +1952,8 @@ impl BufferView { .selections(ctx.as_ref()) .iter() .map(|s| s.range(buffer).sorted()) - .collect::>(); - self.display_map.fold(ranges, ctx.as_ref()).unwrap(); - ctx.notify(); + .collect(); + self.fold_ranges(ranges, ctx); } fn fold_ranges(&mut self, ranges: Vec>, ctx: &mut ViewContext) { From 3c3cf3b7c532d639c61e80d36e21b1ca20aadc2b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 May 2021 09:52:15 -0700 Subject: [PATCH 24/27] Keep weak handles to workspace items --- gpui/src/app.rs | 11 +++- zed/src/workspace.rs | 124 +++++++++++++++++++++++++------------------ 2 files changed, 83 insertions(+), 52 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index cc844581ae71c04aea3b5a756e487b6c312d59ac..2fe6af421230e27b67d83449c1e18a7562960745 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2100,7 +2100,7 @@ impl ModelHandle { } } - fn downgrade(&self) -> WeakModelHandle { + pub fn downgrade(&self) -> WeakModelHandle { WeakModelHandle::new(self.model_id) } @@ -2268,6 +2268,15 @@ impl WeakModelHandle { } } +impl Clone for WeakModelHandle { + fn clone(&self) -> Self { + Self { + model_id: self.model_id, + model_type: PhantomData, + } + } +} + pub struct ViewHandle { window_id: usize, view_id: usize, diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 12888237e3919e7f800ef093692cf30d17f85731..441e0d291d46541247d0460b489b69b57e287df5 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -1,14 +1,28 @@ pub mod pane; pub mod pane_group; -pub use pane::*; -pub use pane_group::*; - use crate::{ + editor::{Buffer, BufferView}, settings::Settings, + time::ReplicaId, watch::{self, Receiver}, - worktree::FileHandle, + worktree::{FileHandle, Worktree, WorktreeHandle}, +}; +use futures_core::{future::LocalBoxFuture, Future}; +use gpui::{ + color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, + ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, PathPromptOptions, View, + ViewContext, ViewHandle, WeakModelHandle, +}; +use log::error; +pub use pane::*; +pub use pane_group::*; +use smol::prelude::*; +use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::{hash_map::Entry, HashSet}, + path::Path, + sync::Arc, }; -use std::{collections::HashMap, fmt, path::PathBuf}; pub fn init(app: &mut MutableAppContext) { app.add_global_action("workspace:open", open); @@ -23,24 +37,6 @@ pub fn init(app: &mut MutableAppContext) { ]); pane::init(app); } -use crate::{ - editor::{Buffer, BufferView}, - time::ReplicaId, - worktree::{Worktree, WorktreeHandle}, -}; -use futures_core::{future::LocalBoxFuture, Future}; -use gpui::{ - color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, - ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, PathPromptOptions, View, - ViewContext, ViewHandle, -}; -use log::error; -use smol::prelude::*; -use std::{ - collections::{hash_map::Entry, HashSet}, - path::Path, - sync::Arc, -}; pub struct OpenParams { pub paths: Vec, @@ -137,15 +133,19 @@ pub trait ItemView: View { } pub trait ItemHandle: Send + Sync { - fn id(&self) -> usize; + fn boxed_clone(&self) -> Box; + fn downgrade(&self) -> Box; +} + +pub trait WeakItemHandle: Send + Sync { fn file<'a>(&'a self, ctx: &'a AppContext) -> Option<&'a FileHandle>; fn add_view( &self, window_id: usize, settings: watch::Receiver, app: &mut MutableAppContext, - ) -> Box; - fn boxed_clone(&self) -> Box; + ) -> Option>; + fn alive(&self, ctx: &AppContext) -> bool; } pub trait ItemViewHandle: Send + Sync { @@ -165,25 +165,37 @@ pub trait ItemViewHandle: Send + Sync { } impl ItemHandle for ModelHandle { - fn id(&self) -> usize { - self.id() + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn downgrade(&self) -> Box { + Box::new(self.downgrade()) } +} +impl WeakItemHandle for WeakModelHandle { fn file<'a>(&'a self, ctx: &'a AppContext) -> Option<&'a FileHandle> { - self.read(ctx).file() + self.upgrade(ctx).and_then(|h| h.read(ctx).file()) } fn add_view( &self, window_id: usize, - settings: watch::Receiver, - app: &mut MutableAppContext, - ) -> Box { - Box::new(app.add_view(window_id, |ctx| T::build_view(self.clone(), settings, ctx))) + settings: Receiver, + ctx: &mut MutableAppContext, + ) -> Option> { + if let Some(handle) = self.upgrade(ctx.as_ref()) { + Some(Box::new(ctx.add_view(window_id, |ctx| { + T::build_view(handle, settings, ctx) + }))) + } else { + None + } } - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) + fn alive(&self, ctx: &AppContext) -> bool { + self.upgrade(ctx).is_some() } } @@ -256,12 +268,6 @@ impl Clone for Box { } } -impl fmt::Debug for Box { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ItemHandle {{id: {}}}", self.id()) - } -} - #[derive(Debug)] pub struct State { pub modal: Option, @@ -276,7 +282,7 @@ pub struct Workspace { active_pane: ViewHandle, replica_id: ReplicaId, worktrees: HashSet>, - items: Vec>, + items: Vec>, loading_items: HashMap< (usize, Arc), postage::watch::Receiver, Arc>>>, @@ -427,7 +433,7 @@ impl Workspace { let buffer = ctx.add_model(|ctx| Buffer::new(self.replica_id, "", ctx)); let buffer_view = ctx.add_view(|ctx| BufferView::for_buffer(buffer.clone(), self.settings.clone(), ctx)); - self.items.push(Box::new(buffer)); + self.items.push(ItemHandle::downgrade(&buffer)); self.add_item(Box::new(buffer_view), ctx); } @@ -451,12 +457,25 @@ impl Workspace { // Otherwise, if this file is already open somewhere in the workspace, // then add another view for it. - if let Some(item) = self.items.iter().find(|item| { - item.file(ctx.as_ref()) - .map_or(false, |f| f.entry_id() == entry) - }) { - self.add_item(item.add_view(window_id, settings, ctx.as_mut()), ctx); - return None; + let mut i = 0; + while i < self.items.len() { + let item = &self.items[i]; + if item.alive(ctx.as_ref()) { + if item + .file(ctx.as_ref()) + .map_or(false, |f| f.entry_id() == entry) + { + self.add_item( + item.add_view(window_id, settings.clone(), ctx.as_mut()) + .unwrap(), + ctx, + ); + return None; + } + i += 1; + } else { + self.items.remove(i); + } } let (worktree_id, path) = entry.clone(); @@ -507,8 +526,11 @@ impl Workspace { me.loading_items.remove(&entry); match load_result { Ok(item) => { - me.items.push(item.clone()); - let view = item.add_view(window_id, settings, ctx.as_mut()); + let weak_item = item.downgrade(); + let view = weak_item + .add_view(window_id, settings, ctx.as_mut()) + .unwrap(); + me.items.push(weak_item); me.add_item(view, ctx); } Err(error) => { From 29d44c8a100e9f6c51d61dcb18bbf01fe890f896 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 May 2021 09:53:07 -0700 Subject: [PATCH 25/27] Go back to using upstream core-foundation-rs --- Cargo.lock | 12 ++++++------ Cargo.toml | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e17c480da60b752d7f353c7915719691df77c093..1fba36fd1210a8ddb0427046923b468f11463463 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,7 @@ dependencies = [ [[package]] name = "cocoa" version = "0.24.0" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" +source = "git+https://github.com/servo/core-foundation-rs?rev=025dcb3c0d1ef01530f57ef65f3b1deb948f5737#025dcb3c0d1ef01530f57ef65f3b1deb948f5737" dependencies = [ "bitflags 1.2.1", "block", @@ -464,7 +464,7 @@ dependencies = [ [[package]] name = "cocoa-foundation" version = "0.1.0" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" +source = "git+https://github.com/servo/core-foundation-rs?rev=025dcb3c0d1ef01530f57ef65f3b1deb948f5737#025dcb3c0d1ef01530f57ef65f3b1deb948f5737" dependencies = [ "bitflags 1.2.1", "block", @@ -499,7 +499,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" version = "0.9.1" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" +source = "git+https://github.com/servo/core-foundation-rs?rev=025dcb3c0d1ef01530f57ef65f3b1deb948f5737#025dcb3c0d1ef01530f57ef65f3b1deb948f5737" dependencies = [ "core-foundation-sys", "libc", @@ -508,12 +508,12 @@ dependencies = [ [[package]] name = "core-foundation-sys" version = "0.8.2" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" +source = "git+https://github.com/servo/core-foundation-rs?rev=025dcb3c0d1ef01530f57ef65f3b1deb948f5737#025dcb3c0d1ef01530f57ef65f3b1deb948f5737" [[package]] name = "core-graphics" version = "0.22.2" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" +source = "git+https://github.com/servo/core-foundation-rs?rev=025dcb3c0d1ef01530f57ef65f3b1deb948f5737#025dcb3c0d1ef01530f57ef65f3b1deb948f5737" dependencies = [ "bitflags 1.2.1", "core-foundation", @@ -525,7 +525,7 @@ dependencies = [ [[package]] name = "core-graphics-types" version = "0.1.1" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a" +source = "git+https://github.com/servo/core-foundation-rs?rev=025dcb3c0d1ef01530f57ef65f3b1deb948f5737#025dcb3c0d1ef01530f57ef65f3b1deb948f5737" dependencies = [ "bitflags 1.2.1", "core-foundation", diff --git a/Cargo.toml b/Cargo.toml index e1729a3a46d0180f3ab4baaa7a496bf980a02f78..80fbb251e5707b8b727a59aaf58ccafb0b79d75f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,10 @@ members = ["zed", "gpui", "fsevent", "scoped_pool"] async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"} # TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457 -cocoa = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} -cocoa-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} -core-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} -core-graphics = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"} +cocoa = {git = "https://github.com/servo/core-foundation-rs", rev = "025dcb3c0d1ef01530f57ef65f3b1deb948f5737"} +cocoa-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "025dcb3c0d1ef01530f57ef65f3b1deb948f5737"} +core-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "025dcb3c0d1ef01530f57ef65f3b1deb948f5737"} +core-graphics = {git = "https://github.com/servo/core-foundation-rs", rev = "025dcb3c0d1ef01530f57ef65f3b1deb948f5737"} [profile.dev] split-debuginfo = "unpacked" From 4b0bd6b8e323e6699637f3115454d8f3e9efd51b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 May 2021 11:21:49 -0700 Subject: [PATCH 26/27] Clean up Workspace::open_entry --- zed/src/workspace.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 441e0d291d46541247d0460b489b69b57e287df5..00166073e2a1c99fcac7cf2c27c5511447693427 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -434,7 +434,7 @@ impl Workspace { let buffer_view = ctx.add_view(|ctx| BufferView::for_buffer(buffer.clone(), self.settings.clone(), ctx)); self.items.push(ItemHandle::downgrade(&buffer)); - self.add_item(Box::new(buffer_view), ctx); + self.add_item_view(Box::new(buffer_view), ctx); } #[must_use] @@ -452,30 +452,30 @@ impl Workspace { return None; } - let window_id = ctx.window_id(); - let settings = self.settings.clone(); - // Otherwise, if this file is already open somewhere in the workspace, // then add another view for it. - let mut i = 0; - while i < self.items.len() { - let item = &self.items[i]; + let settings = self.settings.clone(); + let mut view_for_existing_item = None; + self.items.retain(|item| { if item.alive(ctx.as_ref()) { - if item - .file(ctx.as_ref()) - .map_or(false, |f| f.entry_id() == entry) + if view_for_existing_item.is_none() + && item + .file(ctx.as_ref()) + .map_or(false, |f| f.entry_id() == entry) { - self.add_item( - item.add_view(window_id, settings.clone(), ctx.as_mut()) + view_for_existing_item = Some( + item.add_view(ctx.window_id(), settings.clone(), ctx.as_mut()) .unwrap(), - ctx, ); - return None; } - i += 1; + true } else { - self.items.remove(i); + false } + }); + if let Some(view) = view_for_existing_item { + self.add_item_view(view, ctx); + return None; } let (worktree_id, path) = entry.clone(); @@ -528,10 +528,10 @@ impl Workspace { Ok(item) => { let weak_item = item.downgrade(); let view = weak_item - .add_view(window_id, settings, ctx.as_mut()) + .add_view(ctx.window_id(), settings, ctx.as_mut()) .unwrap(); me.items.push(weak_item); - me.add_item(view, ctx); + me.add_item_view(view, ctx); } Err(error) => { log::error!("error opening item: {}", error); @@ -648,7 +648,7 @@ impl Workspace { self.activate_pane(new_pane.clone(), ctx); if let Some(item) = pane.read(ctx).active_item() { if let Some(clone) = item.clone_on_split(ctx.as_mut()) { - self.add_item(clone, ctx); + self.add_item_view(clone, ctx); } } self.center @@ -673,7 +673,7 @@ impl Workspace { &self.active_pane } - fn add_item(&self, item: Box, ctx: &mut ViewContext) { + fn add_item_view(&self, item: Box, ctx: &mut ViewContext) { let active_pane = self.active_pane(); item.set_parent_pane(&active_pane, ctx.as_mut()); active_pane.update(ctx, |pane, ctx| { From d8d36a32bfc09f6dc7662301bc8db09694837b3f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 May 2021 13:51:17 -0700 Subject: [PATCH 27/27] Add emacs key bindings for up/down/left/right --- zed/src/editor/buffer_view.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 605744b37a2987e5ebde032a80842b1809668fcb..2be7d19f6afb09d13726febd034ecf4ad3a68cd5 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -67,6 +67,10 @@ pub fn init(app: &mut MutableAppContext) { Binding::new("down", "buffer:move_down", Some("BufferView")), Binding::new("left", "buffer:move_left", Some("BufferView")), Binding::new("right", "buffer:move_right", Some("BufferView")), + Binding::new("ctrl-p", "buffer:move_up", Some("BufferView")), + Binding::new("ctrl-n", "buffer:move_down", Some("BufferView")), + Binding::new("ctrl-b", "buffer:move_left", Some("BufferView")), + Binding::new("ctrl-f", "buffer:move_right", Some("BufferView")), Binding::new( "alt-left", "buffer:move_to_previous_word_boundary",