Detailed changes
@@ -348,7 +348,7 @@ impl Editor {
cx: &mut ViewContext<Self>,
) -> Self {
let display_map =
- cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.borrow().clone(), None, cx));
+ cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.clone(), 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)
@@ -5,6 +5,7 @@ mod wrap_map;
use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint};
use fold_map::FoldMap;
use gpui::{Entity, ModelContext, ModelHandle};
+use postage::watch;
use std::ops::Range;
use tab_map::TabMap;
pub use wrap_map::BufferRows;
@@ -24,12 +25,12 @@ impl Entity for DisplayMap {
impl DisplayMap {
pub fn new(
buffer: ModelHandle<Buffer>,
- settings: Settings,
+ settings: watch::Receiver<Settings>,
wrap_width: Option<f32>,
cx: &mut ModelContext<Self>,
) -> Self {
let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx);
- let (tab_map, snapshot) = TabMap::new(snapshot, settings.tab_size);
+ 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));
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
DisplayMap {
@@ -387,6 +388,7 @@ mod tests {
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
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 (_observer, notifications) = Observer::new(&map, &mut cx);
@@ -543,7 +545,8 @@ mod tests {
let text = "one two three four five\nsix seven eight";
let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx));
- let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, 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 snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx));
assert_eq!(
@@ -599,23 +602,28 @@ mod tests {
snapshot.chunks_at(1).collect::<String>(),
"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;
+
+ let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx));
+ assert_eq!(
+ snapshot.chunks_at(1).collect::<String>(),
+ "three \nfour five\nsix and \nseven \neight"
+ )
}
#[gpui::test]
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 = cx.add_model(|cx| {
- DisplayMap::new(
- buffer.clone(),
- Settings {
- tab_size: 4,
- ..Settings::test(cx)
- },
- None,
- cx,
- )
+ let settings = watch::channel_with(Settings {
+ tab_size: 4,
+ ..Settings::test(cx)
});
+ let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx));
buffer.update(cx, |buffer, cx| {
buffer.edit(
vec![
@@ -687,17 +695,13 @@ mod tests {
});
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
- let map = cx.add_model(|cx| {
- DisplayMap::new(
- buffer,
- Settings {
- tab_size: 2,
- ..Settings::test(cx)
- },
- None,
- cx,
- )
+ 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));
assert_eq!(
cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
vec![
@@ -778,13 +782,15 @@ mod tests {
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
let font_cache = cx.font_cache();
- let settings = Settings {
- tab_size: 4,
- buffer_font_family: font_cache.load_family(&["Courier"]).unwrap(),
- buffer_font_size: 16.0,
- ..cx.read(Settings::test)
- };
- let map = cx.add_model(|cx| DisplayMap::new(buffer, settings, Some(40.0), cx));
+ 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));
assert_eq!(
cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
[
@@ -819,17 +825,11 @@ 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 map = cx.add_model(|cx| {
- DisplayMap::new(
- buffer.clone(),
- Settings {
- tab_size: 4,
- ..Settings::test(cx)
- },
- None,
- cx,
- )
+ let settings = watch::channel_with(Settings {
+ tab_size: 4,
+ ..Settings::test(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,17 +863,11 @@ 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 map = cx.add_model(|cx| {
- DisplayMap::new(
- buffer.clone(),
- Settings {
- tab_size: 4,
- ..Settings::test(cx)
- },
- None,
- cx,
- )
+ let settings = watch::channel_with(Settings {
+ tab_size: 4,
+ ..Settings::test(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!(
@@ -930,17 +924,11 @@ 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 = cx.add_model(|cx| {
- DisplayMap::new(
- buffer.clone(),
- Settings {
- tab_size: 4,
- ..Settings::test(cx)
- },
- None,
- cx,
- )
+ let settings = watch::channel_with(Settings {
+ tab_size: 4,
+ ..Settings::test(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)
@@ -9,6 +9,7 @@ use gpui::{
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};
@@ -17,7 +18,8 @@ pub struct WrapMap {
pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
wrap_width: Option<f32>,
background_task: Option<Task<()>>,
- settings: Settings,
+ _watch_settings: Task<()>,
+ settings: watch::Receiver<Settings>,
}
impl Entity for WrapMap {
@@ -74,18 +76,39 @@ pub struct BufferRows<'a> {
impl WrapMap {
pub fn new(
tab_snapshot: TabSnapshot,
- settings: Settings,
+ settings: watch::Receiver<Settings>,
wrap_width: Option<f32>,
cx: &mut ModelContext<Self>,
) -> 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 {
- background_task: None,
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);
+
this
}
@@ -111,17 +134,25 @@ impl WrapMap {
}
self.wrap_width = wrap_width;
+ self.rewrap(cx);
+ true
+ }
+
+ fn rewrap(&mut self, cx: &mut ModelContext<Self>) {
self.background_task.take();
- if let Some(wrap_width) = wrap_width {
+ 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 task = cx.background().spawn(async move {
- let font_id = font_cache
- .select_font(settings.buffer_font_family, &Default::default())
- .unwrap();
- let mut line_wrapper = font_cache.line_wrapper(font_id, settings.buffer_font_size);
+ 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 tab_snapshot = new_snapshot.tab_snapshot.clone();
let range = TabPoint::zero()..tab_snapshot.max_point();
new_snapshot
@@ -144,6 +175,7 @@ impl WrapMap {
{
Ok(snapshot) => {
self.snapshot = snapshot;
+ cx.notify();
}
Err(wrap_task) => {
self.background_task = Some(cx.spawn(|this, mut cx| async move {
@@ -166,8 +198,6 @@ impl WrapMap {
.push(Transform::isomorphic(summary), &());
}
}
-
- true
}
fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
@@ -194,11 +224,14 @@ impl WrapMap {
let font_cache = cx.font_cache().clone();
let settings = self.settings.clone();
let update_task = cx.background().spawn(async move {
- let font_id = font_cache
- .select_font(settings.buffer_font_family, &Default::default())
- .unwrap();
- let mut line_wrapper =
- font_cache.line_wrapper(font_id, settings.buffer_font_size);
+ 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)
+ };
+
for (tab_snapshot, edits) in pending_edits {
snapshot
.update(tab_snapshot, &edits, wrap_width, &mut line_wrapper)
@@ -942,9 +975,6 @@ mod tests {
folds_snapshot.text()
);
log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
- let wrap_map = cx
- .add_model(|cx| WrapMap::new(tabs_snapshot.clone(), settings.clone(), wrap_width, cx));
- let (_observer, notifications) = Observer::new(&wrap_map, &mut cx);
let font_id = font_cache
.select_font(settings.buffer_font_family, &Default::default())
@@ -953,6 +983,11 @@ mod tests {
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 (_observer, notifications) = Observer::new(&wrap_map, &mut cx);
+
if wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) {
notifications.recv().await.unwrap();
}
@@ -8,6 +8,7 @@ use gpui::{
PathBuilder,
},
json::{self, ToJson},
+ keymap::Keystroke,
text_layout::{self, TextLayoutCache},
AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext,
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
@@ -127,14 +128,14 @@ impl EditorElement {
}
}
- fn key_down(&self, chars: &str, cx: &mut EventContext) -> bool {
+ fn key_down(&self, chars: &str, keystroke: &Keystroke, cx: &mut EventContext) -> bool {
let view = self.view.upgrade(cx.app).unwrap();
if view.is_focused(cx.app) {
if chars.is_empty() {
false
} else {
- if chars.chars().any(|c| c.is_control()) {
+ if chars.chars().any(|c| c.is_control()) || keystroke.cmd || keystroke.ctrl {
false
} else {
cx.dispatch_action(Insert(chars.to_string()));
@@ -629,7 +630,9 @@ impl Element for EditorElement {
delta,
precise,
} => self.scroll(*position, *delta, *precise, layout, paint, cx),
- Event::KeyDown { chars, .. } => self.key_down(chars, cx),
+ Event::KeyDown {
+ chars, keystroke, ..
+ } => self.key_down(chars, keystroke, cx),
_ => false,
}
} else {
@@ -187,7 +187,7 @@ mod tests {
#[gpui::test]
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
- let settings = test_app_state(cx).settings.borrow().clone();
+ let settings = test_app_state(cx).settings.clone();
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 snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
@@ -22,7 +22,7 @@ pub mod worktree;
use crate::util::TryFutureExt;
use channel::ChannelList;
-use gpui::{action, ModelHandle};
+use gpui::{action, keymap::Binding, ModelHandle};
use parking_lot::Mutex;
use postage::watch;
use std::sync::Arc;
@@ -32,6 +32,9 @@ pub use settings::Settings;
action!(About);
action!(Quit);
action!(Authenticate);
+action!(AdjustBufferFontSize, f32);
+
+const MIN_FONT_SIZE: f32 = 6.0;
pub struct AppState {
pub settings_tx: Arc<Mutex<watch::Sender<Settings>>>,
@@ -54,6 +57,22 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
.detach();
}
});
+
+ cx.add_global_action({
+ let settings_tx = app_state.settings_tx.clone();
+
+ move |action: &AdjustBufferFontSize, cx| {
+ let mut settings_tx = settings_tx.lock();
+ let new_size = (settings_tx.borrow().buffer_font_size + action.0).max(MIN_FONT_SIZE);
+ settings_tx.borrow_mut().buffer_font_size = new_size;
+ cx.refresh_windows();
+ }
+ });
+
+ cx.add_bindings(vec![
+ Binding::new("cmd-=", AdjustBufferFontSize(1.), None),
+ Binding::new("cmd--", AdjustBufferFontSize(-1.), None),
+ ])
}
fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {