diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 4b18af06b4bdb0fef7c3fb47e819fa3153e6c8f2..ebe6c89a8ace2a965138658a8faaccab66b2f1ef 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2335,6 +2335,16 @@ impl ReadModel for RenderContext<'_, V> { } } +impl UpdateModel for RenderContext<'_, V> { + fn update_model(&mut self, handle: &ModelHandle, update: F) -> S + where + T: Entity, + F: FnOnce(&mut T, &mut ModelContext) -> S, + { + self.app.update_model(handle, update) + } +} + impl AsRef for ViewContext<'_, M> { fn as_ref(&self) -> &AppContext { &self.app.cx diff --git a/zed/src/editor.rs b/zed/src/editor.rs index c27d91d3404d211ce43263cbc0896494c2c89258..1b0eb4852b8e528c03adcaac7aaaec9498f18b5e 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -17,9 +17,9 @@ pub use display_map::DisplayPoint; use display_map::*; pub use element::*; use gpui::{ - action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, - keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, - ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle, + action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, keymap::Binding, + text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, + MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle, }; use postage::watch; use serde::{Deserialize, Serialize}; @@ -372,8 +372,17 @@ impl Editor { build_style: Rc EditorStyle>>, cx: &mut ViewContext, ) -> Self { - let display_map = - cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.clone(), None, cx)); + let style = build_style.borrow_mut()(cx); + let display_map = cx.add_model(|cx| { + DisplayMap::new( + buffer.clone(), + settings.borrow().tab_size, + style.text.font_id, + style.text.font_size, + None, + cx, + ) + }); cx.observe(&buffer, Self::on_buffer_changed).detach(); cx.subscribe(&buffer, Self::on_buffer_event).detach(); cx.observe(&display_map, Self::on_display_map_changed) @@ -2452,6 +2461,9 @@ impl Entity for Editor { impl View for Editor { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { let style = self.build_style.borrow_mut()(cx); + self.display_map.update(cx, |map, cx| { + map.set_font(style.text.font_id, style.text.font_size, cx) + }); EditorElement::new(self.handle.clone(), style).boxed() } @@ -4257,12 +4269,33 @@ mod tests { settings: watch::Receiver, cx: &mut ViewContext, ) -> Editor { - Editor::for_buffer( - buffer, - settings, - move |cx| EditorStyle::test(cx.font_cache()), - cx, - ) + let style = { + let font_cache = cx.font_cache(); + let settings = settings.borrow(); + EditorStyle { + text: TextStyle { + color: Default::default(), + font_family_name: font_cache.family_name(settings.buffer_font_family).unwrap(), + font_family_id: settings.buffer_font_family, + font_id: font_cache + .select_font(settings.buffer_font_family, &Default::default()) + .unwrap(), + font_size: settings.buffer_font_size, + font_properties: Default::default(), + underline: false, + }, + placeholder_text: None, + background: Default::default(), + selection: Default::default(), + gutter_background: Default::default(), + active_line_background: Default::default(), + line_number: Default::default(), + line_number_active: Default::default(), + guest_selections: Default::default(), + } + }; + + Editor::for_buffer(buffer, settings, move |_| style.clone(), cx) } } diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index cdfc17f46c28f84c14336630142c501e2bb5835c..e4b8cc0886603f1257bac097445a08db7f0b9871 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -2,10 +2,9 @@ mod fold_map; mod tab_map; mod wrap_map; -use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint}; +use super::{buffer, Anchor, Bias, Buffer, Point, ToOffset, ToPoint}; use fold_map::FoldMap; -use gpui::{Entity, ModelContext, ModelHandle}; -use postage::watch; +use gpui::{fonts::FontId, Entity, ModelContext, ModelHandle}; use std::ops::Range; use tab_map::TabMap; use wrap_map::WrapMap; @@ -25,13 +24,16 @@ impl Entity for DisplayMap { impl DisplayMap { pub fn new( buffer: ModelHandle, - settings: watch::Receiver, + tab_size: usize, + font_id: FontId, + font_size: f32, wrap_width: Option, cx: &mut ModelContext, ) -> Self { let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx); - let (tab_map, snapshot) = TabMap::new(snapshot, settings.borrow().tab_size); - let wrap_map = cx.add_model(|cx| WrapMap::new(snapshot, settings, wrap_width, cx)); + let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); + let wrap_map = + cx.add_model(|cx| WrapMap::new(snapshot, font_id, font_size, wrap_width, cx)); cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach(); DisplayMap { buffer, @@ -85,6 +87,11 @@ impl DisplayMap { .update(cx, |map, cx| map.sync(snapshot, edits, cx)); } + pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext) { + self.wrap_map + .update(cx, |map, cx| map.set_font(font_id, font_size, cx)); + } + pub fn set_wrap_width(&self, width: Option, cx: &mut ModelContext) -> bool { self.wrap_map .update(cx, |map, cx| map.set_wrap_width(width, cx)) @@ -367,12 +374,12 @@ mod tests { .unwrap_or(10); let font_cache = cx.font_cache().clone(); - let settings = Settings { - tab_size: rng.gen_range(1..=4), - buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - buffer_font_size: 14.0, - ..cx.read(Settings::test) - }; + let tab_size = rng.gen_range(1..=4); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; let max_wrap_width = 300.0; let mut wrap_width = if rng.gen_bool(0.1) { None @@ -380,7 +387,7 @@ mod tests { Some(rng.gen_range(0.0..=max_wrap_width)) }; - log::info!("tab size: {}", settings.tab_size); + log::info!("tab size: {}", tab_size); log::info!("wrap width: {:?}", wrap_width); let buffer = cx.add_model(|cx| { @@ -388,9 +395,10 @@ mod tests { let text = RandomCharIter::new(&mut rng).take(len).collect::(); Buffer::new(0, text, cx) }); - let settings = watch::channel_with(settings).1; - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx)); + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx) + }); let (_observer, notifications) = Observer::new(&map, &mut cx); let mut fold_count = 0; @@ -529,26 +537,27 @@ mod tests { } #[gpui::test] - async fn test_soft_wraps(mut cx: gpui::TestAppContext) { + fn test_soft_wraps(cx: &mut MutableAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); cx.foreground().forbid_parking(); let font_cache = cx.font_cache(); - let settings = Settings { - buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - buffer_font_size: 12.0, - tab_size: 4, - ..cx.read(Settings::test) - }; + let tab_size = 4; + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 12.0; let wrap_width = Some(64.); let text = "one two three four five\nsix seven eight"; let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx)); - let (mut settings_tx, settings_rx) = watch::channel_with(settings); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings_rx, wrap_width, cx)); + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx) + }); - let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx)); + let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( snapshot.chunks_at(0).collect::(), "one two \nthree four \nfive\nsix seven \neight" @@ -592,23 +601,21 @@ mod tests { (DisplayPoint::new(2, 4), SelectionGoal::Column(10)) ); - buffer.update(&mut cx, |buffer, cx| { + buffer.update(cx, |buffer, cx| { let ix = buffer.text().find("seven").unwrap(); buffer.edit(vec![ix..ix], "and ", cx); }); - let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx)); + let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( snapshot.chunks_at(1).collect::(), "three four \nfive\nsix and \nseven eight" ); // Re-wrap on font size changes - settings_tx.borrow_mut().buffer_font_size += 3.; - - map.next_notification(&mut cx).await; + map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx)); - let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx)); + let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( snapshot.chunks_at(1).collect::(), "three \nfour five\nsix and \nseven \neight" @@ -619,11 +626,16 @@ 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 settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + let tab_size = 4; + let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let font_id = cx + .font_cache() + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); buffer.update(cx, |buffer, cx| { buffer.edit( vec![ @@ -695,13 +707,16 @@ mod tests { }); buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; - let settings = cx.update(|cx| { - watch::channel_with(Settings { - tab_size: 2, - ..Settings::test(cx) - }) - }); - let map = cx.add_model(|cx| DisplayMap::new(buffer, settings.1, None, cx)); + let tab_size = 2; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + + let map = + cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); assert_eq!( cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)), vec![ @@ -782,15 +797,16 @@ mod tests { buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; let font_cache = cx.font_cache(); - let settings = cx.update(|cx| { - watch::channel_with(Settings { - tab_size: 4, - buffer_font_family: font_cache.load_family(&["Courier"]).unwrap(), - buffer_font_size: 16.0, - ..Settings::test(cx) - }) - }); - let map = cx.add_model(|cx| DisplayMap::new(buffer, settings.1, Some(40.0), cx)); + + let tab_size = 4; + let family_id = font_cache.load_family(&["Courier"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 16.0; + + let map = cx + .add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), cx)); assert_eq!( cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)), [ @@ -825,11 +841,17 @@ mod tests { let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n"; let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + + let tab_size = 4; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); let map = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!(map.text(), display_text); @@ -863,11 +885,17 @@ mod tests { fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) { let text = "✅\t\tα\nβ\t\n🏀β\t\tγ"; let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + let tab_size = 4; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); let map = map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!(map.text(), "✅ α\nβ \n🏀β γ"); assert_eq!( @@ -924,11 +952,16 @@ 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 settings = watch::channel_with(Settings { - tab_size: 4, - ..Settings::test(cx) + let tab_size = 4; + let font_cache = cx.font_cache(); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let map = cx.add_model(|cx| { + DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) }); - let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx)); assert_eq!( map.update(cx, |map, cx| map.snapshot(cx)).max_point(), DisplayPoint::new(1, 11) diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index d648a052aab0c6fb4ffc626d803d47983568d3a7..86e01f7e5695f1e4aa4ae1ac7dfaedd170b5c6a8 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -2,14 +2,14 @@ use super::{ fold_map, tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary}, }; -use crate::{editor::Point, settings::HighlightId, util::Bias, Settings}; +use crate::{editor::Point, settings::HighlightId, util::Bias}; use gpui::{ + fonts::FontId, sum_tree::{self, Cursor, SumTree}, text_layout::LineWrapper, Entity, ModelContext, Task, }; use lazy_static::lazy_static; -use postage::{prelude::Stream, watch}; use smol::future::yield_now; use std::{collections::VecDeque, ops::Range, time::Duration}; @@ -18,8 +18,7 @@ pub struct WrapMap { pending_edits: VecDeque<(TabSnapshot, Vec)>, wrap_width: Option, background_task: Option>, - _watch_settings: Task<()>, - settings: watch::Receiver, + font: (FontId, f32), } impl Entity for WrapMap { @@ -76,36 +75,17 @@ pub struct BufferRows<'a> { impl WrapMap { pub fn new( tab_snapshot: TabSnapshot, - settings: watch::Receiver, + font_id: FontId, + font_size: f32, wrap_width: Option, cx: &mut ModelContext, ) -> Self { - let _watch_settings = cx.spawn_weak({ - let mut prev_font = ( - settings.borrow().buffer_font_size, - settings.borrow().buffer_font_family, - ); - let mut settings = settings.clone(); - move |this, mut cx| async move { - while let Some(settings) = settings.recv().await { - if let Some(this) = this.upgrade(&cx) { - let font = (settings.buffer_font_size, settings.buffer_font_family); - if font != prev_font { - prev_font = font; - this.update(&mut cx, |this, cx| this.rewrap(cx)); - } - } - } - } - }); - let mut this = Self { + font: (font_id, font_size), wrap_width: None, pending_edits: Default::default(), snapshot: Snapshot::new(tab_snapshot), - settings, background_task: None, - _watch_settings, }; this.set_wrap_width(wrap_width, cx); @@ -128,6 +108,13 @@ impl WrapMap { self.snapshot.clone() } + pub fn set_font(&mut self, font_id: FontId, font_size: f32, cx: &mut ModelContext) { + if (font_id, font_size) != self.font { + self.font = (font_id, font_size); + self.rewrap(cx) + } + } + pub fn set_wrap_width(&mut self, wrap_width: Option, cx: &mut ModelContext) -> bool { if wrap_width == self.wrap_width { return false; @@ -144,15 +131,9 @@ impl WrapMap { if let Some(wrap_width) = self.wrap_width { let mut new_snapshot = self.snapshot.clone(); let font_cache = cx.font_cache().clone(); - let settings = self.settings.clone(); + let (font_id, font_size) = self.font; let task = cx.background().spawn(async move { - let mut line_wrapper = { - let settings = settings.borrow(); - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - font_cache.line_wrapper(font_id, settings.buffer_font_size) - }; + let mut line_wrapper = font_cache.line_wrapper(font_id, font_size); let tab_snapshot = new_snapshot.tab_snapshot.clone(); let range = TabPoint::zero()..tab_snapshot.max_point(); new_snapshot @@ -222,15 +203,9 @@ impl WrapMap { let pending_edits = self.pending_edits.clone(); let mut snapshot = self.snapshot.clone(); let font_cache = cx.font_cache().clone(); - let settings = self.settings.clone(); + let (font_id, font_size) = self.font; let update_task = cx.background().spawn(async move { - let mut line_wrapper = { - let settings = settings.borrow(); - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - font_cache.line_wrapper(font_id, settings.buffer_font_size) - }; + let mut line_wrapper = font_cache.line_wrapper(font_id, font_size); for (tab_snapshot, edits) in pending_edits { snapshot @@ -950,13 +925,14 @@ mod tests { } else { Some(rng.gen_range(0.0..=1000.0)) }; - let settings = Settings { - tab_size: rng.gen_range(1..=4), - buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), - buffer_font_size: 14.0, - ..cx.read(Settings::test) - }; - log::info!("Tab size: {}", settings.tab_size); + let tab_size = rng.gen_range(1..=4); + let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let font_id = font_cache + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + + log::info!("Tab size: {}", tab_size); log::info!("Wrap width: {:?}", wrap_width); let buffer = cx.add_model(|cx| { @@ -965,7 +941,7 @@ mod tests { Buffer::new(0, text, cx) }); let (mut fold_map, folds_snapshot) = cx.read(|cx| FoldMap::new(buffer.clone(), cx)); - let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), settings.tab_size); + let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size); log::info!( "Unwrapped text (no folds): {:?}", buffer.read_with(&cx, |buf, _| buf.text()) @@ -976,16 +952,13 @@ mod tests { ); log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text()); - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - let mut line_wrapper = LineWrapper::new(font_id, settings.buffer_font_size, font_system); + let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system); let unwrapped_text = tabs_snapshot.text(); let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); - let settings = watch::channel_with(settings).1; - let wrap_map = cx - .add_model(|cx| WrapMap::new(tabs_snapshot.clone(), settings.clone(), wrap_width, cx)); + let wrap_map = cx.add_model(|cx| { + WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx) + }); let (_observer, notifications) = Observer::new(&wrap_map, &mut cx); if wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) { diff --git a/zed/src/editor/movement.rs b/zed/src/editor/movement.rs index aec3932a7d9ad96154f44e95783e9d8a70801c61..8f5bc6f20a814536366f9077f1509d611dc1cf29 100644 --- a/zed/src/editor/movement.rs +++ b/zed/src/editor/movement.rs @@ -180,16 +180,21 @@ fn char_kind(c: char) -> CharKind { #[cfg(test)] mod tests { use super::*; - use crate::{ - editor::{display_map::DisplayMap, Buffer}, - test::test_app_state, - }; + use crate::editor::{display_map::DisplayMap, Buffer}; #[gpui::test] fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) { - let settings = test_app_state(cx).settings.clone(); + let tab_size = 4; + let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let font_id = cx + .font_cache() + .select_font(family_id, &Default::default()) + .unwrap(); + let font_size = 14.0; + let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ", cx)); - let display_map = cx.add_model(|cx| DisplayMap::new(buffer, settings, None, cx)); + let display_map = + cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); assert_eq!( prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)).unwrap(),