diff --git a/zed/src/editor.rs b/zed/src/editor.rs index e06aa2a858..d06b4611d4 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -410,7 +410,8 @@ impl Editor { ) -> Self { cx.observe_model(&buffer, Self::on_buffer_changed); cx.subscribe_to_model(&buffer, Self::on_buffer_event); - let display_map = DisplayMap::new(buffer.clone(), settings.borrow().tab_size, cx.as_ref()); + let display_map = + DisplayMap::new(buffer.clone(), settings.borrow().clone(), None, cx.as_ref()); let mut next_selection_id = 0; let selection_set_id = buffer.update(cx, |buffer, cx| { diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index 516dd15fa6..60790cdb9b 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -2,35 +2,36 @@ mod fold_map; mod tab_map; mod wrap_map; -use super::{buffer, Anchor, Bias, Buffer, Point, ToOffset, ToPoint}; +use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint}; use fold_map::FoldMap; pub use fold_map::InputRows; use gpui::{AppContext, ModelHandle}; use std::ops::Range; use tab_map::TabMap; -// use wrap_map::WrapMap; +use wrap_map::WrapMap; pub struct DisplayMap { buffer: ModelHandle, fold_map: FoldMap, tab_map: TabMap, - // wrap_map: WrapMap, + wrap_map: WrapMap, } impl DisplayMap { - pub fn new(buffer: ModelHandle, tab_size: usize, cx: &AppContext) -> Self { - let fold_map = FoldMap::new(buffer.clone(), cx); - let (snapshot, edits) = fold_map.read(cx); - assert_eq!(edits.len(), 0); - let tab_map = TabMap::new(snapshot, tab_size); - // TODO: take `wrap_width` as a parameter. - // let config = { todo!() }; - // let wrap_map = WrapMap::new(snapshot, config, cx); + pub fn new( + buffer: ModelHandle, + settings: Settings, + wrap_width: Option, + cx: &AppContext, + ) -> Self { + let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx); + let (tab_map, snapshot) = TabMap::new(snapshot, settings.tab_size); + let wrap_map = WrapMap::new(snapshot, settings, wrap_width, cx); DisplayMap { buffer, fold_map, tab_map, - // wrap_map, + wrap_map, } } @@ -61,6 +62,10 @@ impl DisplayMap { let (mut fold_map, snapshot, edits) = self.fold_map.write(cx); let edits = fold_map.unfold(ranges, cx); } + + pub fn set_wrap_width(&self, width: Option) { + self.wrap_map.set_wrap_width(width); + } } pub struct DisplayMapSnapshot { @@ -257,7 +262,12 @@ mod tests { fn test_chunks_at(cx: &mut gpui::MutableAppContext) { let text = sample_text(6, 6); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let map = DisplayMap::new(buffer.clone(), 4, cx.as_ref()); + let map = DisplayMap::new( + buffer.clone(), + Settings::new(cx.font_cache()).unwrap().with_tab_size(4), + None, + cx.as_ref(), + ); buffer.update(cx, |buffer, cx| { buffer.edit( vec![ @@ -334,7 +344,14 @@ mod tests { }); buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; - let mut map = cx.read(|cx| DisplayMap::new(buffer, 2, cx)); + let mut map = cx.read(|cx| { + DisplayMap::new( + buffer, + Settings::new(cx.font_cache()).unwrap().with_tab_size(2), + None, + cx, + ) + }); assert_eq!( cx.read(|cx| highlighted_chunks(0..5, &map, &theme, cx)), vec![ @@ -399,7 +416,12 @@ mod tests { let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); let cx = cx.as_ref(); - let map = DisplayMap::new(buffer.clone(), 4, cx); + let map = DisplayMap::new( + buffer.clone(), + Settings::new(cx.font_cache()).unwrap().with_tab_size(4), + None, + cx, + ); let map = map.snapshot(cx); assert_eq!(map.text(), display_text); @@ -434,7 +456,12 @@ mod tests { let text = "✅\t\tα\nβ\t\n🏀β\t\tγ"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); let cx = cx.as_ref(); - let map = DisplayMap::new(buffer.clone(), 4, cx); + let map = DisplayMap::new( + buffer.clone(), + Settings::new(cx.font_cache()).unwrap().with_tab_size(4), + None, + cx, + ); let map = map.snapshot(cx); assert_eq!(map.text(), "✅ α\nβ \n🏀β γ"); @@ -495,7 +522,12 @@ mod tests { #[gpui::test] fn test_max_point(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "aaa\n\t\tbbb", cx)); - let map = DisplayMap::new(buffer.clone(), 4, cx.as_ref()); + let map = DisplayMap::new( + buffer.clone(), + Settings::new(cx.font_cache()).unwrap().with_tab_size(4), + None, + cx.as_ref(), + ); assert_eq!( map.snapshot(cx.as_ref()).max_point(), DisplayPoint::new(1, 11) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 3b61ab177e..800fff06d1 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -161,9 +161,9 @@ pub struct FoldMap { } impl FoldMap { - pub fn new(buffer_handle: ModelHandle, cx: &AppContext) -> Self { + pub fn new(buffer_handle: ModelHandle, cx: &AppContext) -> (Self, Snapshot) { let buffer = buffer_handle.read(cx); - Self { + let this = Self { buffer: buffer_handle, folds: Default::default(), transforms: Mutex::new(SumTree::from_item( @@ -178,7 +178,9 @@ impl FoldMap { )), last_sync: Mutex::new(buffer.version()), version: AtomicUsize::new(0), - } + }; + let (snapshot, _) = this.read(cx); + (this, snapshot) } pub fn read(&self, cx: &AppContext) -> (Snapshot, Vec) { @@ -1105,7 +1107,7 @@ mod tests { #[gpui::test] fn test_basic_folds(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx)); - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let (mut writer, _, _) = map.write(cx.as_ref()); let (snapshot2, edits) = writer.fold( @@ -1180,7 +1182,7 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, "abcdefghijkl", cx)); { - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let (mut writer, _, _) = map.write(cx.as_ref()); writer.fold(vec![5..8], cx.as_ref()); @@ -1201,7 +1203,7 @@ mod tests { } { - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; // Create two adjacent folds. let (mut writer, _, _) = map.write(cx.as_ref()); @@ -1219,7 +1221,7 @@ mod tests { #[gpui::test] fn test_overlapping_folds(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx)); - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let (mut writer, _, _) = map.write(cx.as_ref()); writer.fold( vec![ @@ -1237,7 +1239,7 @@ mod tests { #[gpui::test] fn test_merging_folds_via_edit(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx)); - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let (mut writer, _, _) = map.write(cx.as_ref()); writer.fold( @@ -1260,7 +1262,7 @@ mod tests { #[gpui::test] fn test_folds_in_range(cx: &mut gpui::MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx)); - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let buffer = buffer.read(cx); let (mut writer, _, _) = map.write(cx.as_ref()); @@ -1317,7 +1319,7 @@ mod tests { let text = RandomCharIter::new(&mut rng).take(len).collect::(); Buffer::new(0, text, cx) }); - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let (mut initial_snapshot, _) = map.read(cx.as_ref()); let mut snapshot_edits = Vec::new(); @@ -1537,7 +1539,7 @@ mod tests { let text = sample_text(6, 6) + "\n"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let mut map = FoldMap::new(buffer.clone(), cx.as_ref()); + let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0; let (mut writer, _, _) = map.write(cx.as_ref()); writer.fold( diff --git a/zed/src/editor/display_map/tab_map.rs b/zed/src/editor/display_map/tab_map.rs index c662796c81..5066dd1e77 100644 --- a/zed/src/editor/display_map/tab_map.rs +++ b/zed/src/editor/display_map/tab_map.rs @@ -13,8 +13,9 @@ use std::{ pub struct TabMap(Mutex); impl TabMap { - pub fn new(input: InputSnapshot, tab_size: usize) -> Self { - Self(Mutex::new(Snapshot { input, tab_size })) + pub fn new(input: InputSnapshot, tab_size: usize) -> (Self, Snapshot) { + let snapshot = Snapshot { input, tab_size }; + (Self(Mutex::new(snapshot.clone())), snapshot) } pub fn sync( diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index d305e5ae4b..1d436fe8ac 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -5,8 +5,9 @@ use crate::{ editor::Point, sum_tree::{self, Cursor, SumTree}, util::Bias, + Settings, }; -use gpui::{font_cache::FamilyId, AppContext, FontCache, FontSystem, Task}; +use gpui::{AppContext, FontCache, FontSystem, Task}; use parking_lot::Mutex; use postage::{ prelude::{Sink, Stream}, @@ -208,13 +209,6 @@ struct State { pending_edits: VecDeque<(InputSnapshot, Vec)>, } -#[derive(Clone)] -pub struct Config { - pub wrap_width: f32, - pub font_family: FamilyId, - pub font_size: f32, -} - pub struct WrapMap { state: Mutex, edits_tx: channel::Sender<(InputSnapshot, Vec)>, @@ -223,7 +217,12 @@ pub struct WrapMap { } impl WrapMap { - pub fn new(input: InputSnapshot, config: Config, cx: &AppContext) -> Self { + pub fn new( + input: InputSnapshot, + settings: Settings, + wrap_width: Option, + cx: &AppContext, + ) -> Self { let font_cache = cx.font_cache().clone(); let font_system = cx.platform().fonts(); let snapshot = Snapshot::new(input.clone()); @@ -233,7 +232,8 @@ impl WrapMap { let background_task = { let snapshot = snapshot.clone(); cx.background().spawn(async move { - let mut wrapper = BackgroundWrapper::new(snapshot, config, font_cache, font_system); + let mut wrapper = + BackgroundWrapper::new(snapshot, settings, wrap_width, font_cache, font_system); wrapper.run(input, edits_rx, background_snapshot_tx).await; }) }; @@ -254,11 +254,20 @@ impl WrapMap { .try_send((input.clone(), edits.clone())) .unwrap(); + let mut snapshot = self.state.lock().snapshot.clone(); + let mut background_snapshot = self.background_snapshot.clone(); + cx.background().block_on(Duration::from_millis(5), async { + loop { + snapshot = background_snapshot.recv().await.unwrap(); + if snapshot.input.version() == input.version() { + break; + } + } + }); + let mut state = &mut *self.state.lock(); - state.snapshot = self.background_snapshot.borrow().clone(); - state - .pending_edits - .push_back((input.clone(), edits.clone())); + state.snapshot = snapshot; + state.pending_edits.push_back((input, edits)); while state.pending_edits.front().map_or(false, |(input, _)| { input.version() <= state.snapshot.input.version() }) { @@ -267,20 +276,17 @@ impl WrapMap { for (input, edits) in &state.pending_edits { state.snapshot.interpolate(input.clone(), &edits); } + state.snapshot.clone() + } - let mut background_snapshot = self.background_snapshot.clone(); - let next_snapshot = cx - .background() - .block_on(Duration::from_millis(5), async move { - background_snapshot.recv().await; - }); - - self.state.lock().snapshot.clone() + pub fn set_wrap_width(&self, width: Option) { + todo!() } } struct BackgroundWrapper { - config: Config, + settings: Settings, + wrap_width: Option, font_cache: Arc, font_system: Arc, snapshot: Snapshot, @@ -289,12 +295,14 @@ struct BackgroundWrapper { impl BackgroundWrapper { fn new( snapshot: Snapshot, - config: Config, + settings: Settings, + wrap_width: Option, font_cache: Arc, font_system: Arc, ) -> Self { Self { - config, + settings, + wrap_width, font_cache, font_system, snapshot, @@ -331,10 +339,9 @@ impl BackgroundWrapper { let font_id = self .font_cache - .select_font(self.config.font_family, &Default::default()) + .select_font(self.settings.buffer_font_family, &Default::default()) .unwrap(); - let font_size = self.config.font_size; - let wrap_width = self.config.wrap_width; + let font_size = self.settings.buffer_font_size; #[derive(Debug)] struct RowEdit { @@ -403,15 +410,17 @@ impl BackgroundWrapper { } let mut prev_boundary_ix = 0; - for boundary_ix in self - .font_system - .wrap_line(&line, font_id, font_size, wrap_width) - { - let wrapped = &line[prev_boundary_ix..boundary_ix]; - new_transforms - .push_or_extend(Transform::isomorphic(TextSummary::from(wrapped))); - new_transforms.push_or_extend(Transform::newline()); - prev_boundary_ix = boundary_ix; + if let Some(wrap_width) = self.wrap_width { + for boundary_ix in self + .font_system + .wrap_line(&line, font_id, font_size, wrap_width) + { + let wrapped = &line[prev_boundary_ix..boundary_ix]; + new_transforms + .push_or_extend(Transform::isomorphic(TextSummary::from(wrapped))); + new_transforms.push_or_extend(Transform::newline()); + prev_boundary_ix = boundary_ix; + } } if prev_boundary_ix < line.len() { @@ -574,36 +583,6 @@ mod tests { use rand::prelude::*; use std::env; - #[gpui::test] - async fn test_simple_wraps(mut cx: gpui::TestAppContext) { - let text = "one two three four five\nsix seven eight"; - let font_cache = cx.font_cache().clone(); - let config = Config { - wrap_width: 64., - font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - font_size: 14.0, - }; - - let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx)); - let mut wrap_map = cx.read(|cx| { - let fold_map = FoldMap::new(buffer.clone(), cx); - let (folds_snapshot, edits) = fold_map.read(cx); - let tab_map = TabMap::new(folds_snapshot.clone(), 4); - let (tabs_snapshot, _) = tab_map.sync(folds_snapshot, edits); - WrapMap::new(tabs_snapshot, config, cx) - }); - - wrap_map.background_snapshot.next().await; - let snapshot = wrap_map.background_snapshot.next().await.unwrap(); - - assert_eq!( - snapshot - .chunks_at(OutputPoint(Point::new(0, 3))) - .collect::(), - " two \nthree four \nfive\nsix seven \neight" - ); - } - #[gpui::test] fn test_random_wraps(cx: &mut gpui::MutableAppContext) { let iterations = env::var("ITERATIONS") @@ -629,23 +608,25 @@ mod tests { log::info!("Initial buffer text: {:?}", text); Buffer::new(0, text, cx) }); - let fold_map = FoldMap::new(buffer.clone(), cx.as_ref()); - let (folds_snapshot, edits) = fold_map.read(cx.as_ref()); - let tab_map = TabMap::new(folds_snapshot.clone(), rng.gen_range(1..=4)); - let (tabs_snapshot, _) = tab_map.sync(folds_snapshot, edits); + let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx.as_ref()); + let (tab_map, tabs_snapshot) = + TabMap::new(folds_snapshot.clone(), rng.gen_range(1..=4)); let font_cache = cx.font_cache().clone(); let font_system = cx.platform().fonts(); - let config = Config { - wrap_width: rng.gen_range(100.0..=1000.0), - font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - font_size: 14.0, + let wrap_width = rng.gen_range(100.0..=1000.0); + let settings = Settings { + tab_size: 4, + buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), + buffer_font_size: 14.0, + ..Settings::new(&font_cache).unwrap() }; let font_id = font_cache - .select_font(config.font_family, &Default::default()) + .select_font(settings.buffer_font_family, &Default::default()) .unwrap(); let mut wrapper = BackgroundWrapper::new( Snapshot::new(tabs_snapshot.clone()), - config.clone(), + settings.clone(), + Some(wrap_width), font_cache.clone(), font_system.clone(), ); @@ -656,12 +637,8 @@ mod tests { wrapper.sync(tabs_snapshot.clone(), vec![edit]); let unwrapped_text = tabs_snapshot.text(); - let expected_text = wrap_text( - &unwrapped_text, - config.wrap_width, - font_id, - font_system.as_ref(), - ); + let expected_text = + wrap_text(&unwrapped_text, wrap_width, font_id, font_system.as_ref()); let actual_text = wrapper .snapshot @@ -683,12 +660,8 @@ mod tests { interpolated_snapshot.check_invariants(); let unwrapped_text = snapshot.text(); - let expected_text = wrap_text( - &unwrapped_text, - config.wrap_width, - font_id, - font_system.as_ref(), - ); + let expected_text = + wrap_text(&unwrapped_text, wrap_width, font_id, font_system.as_ref()); wrapper.sync(snapshot, edits); wrapper.snapshot.check_invariants(); let actual_text = wrapper diff --git a/zed/src/lib.rs b/zed/src/lib.rs index efa0dd8288..b8c84feb1a 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -17,8 +17,9 @@ mod util; pub mod workspace; pub mod worktree; +pub use settings::Settings; pub struct AppState { - pub settings: postage::watch::Receiver, + pub settings: postage::watch::Receiver, pub languages: std::sync::Arc, pub rpc_router: std::sync::Arc, pub rpc: rpc::Client, diff --git a/zed/src/settings.rs b/zed/src/settings.rs index 7e60e6da23..0813b09a04 100644 --- a/zed/src/settings.rs +++ b/zed/src/settings.rs @@ -49,6 +49,11 @@ impl Settings { ), }) } + + pub fn with_tab_size(mut self, tab_size: usize) -> Self { + self.tab_size = tab_size; + self + } } impl Theme {