Add WrapMap as a member of DisplayMap

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-07-20 13:03:59 -07:00
parent dbc8fc3bfa
commit 7832562675
7 changed files with 136 additions and 121 deletions

View file

@ -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| {

View file

@ -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<Buffer>,
fold_map: FoldMap,
tab_map: TabMap,
// wrap_map: WrapMap,
wrap_map: WrapMap,
}
impl DisplayMap {
pub fn new(buffer: ModelHandle<Buffer>, 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<Buffer>,
settings: Settings,
wrap_width: Option<f32>,
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<f32>) {
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)

View file

@ -161,9 +161,9 @@ pub struct FoldMap {
}
impl FoldMap {
pub fn new(buffer_handle: ModelHandle<Buffer>, cx: &AppContext) -> Self {
pub fn new(buffer_handle: ModelHandle<Buffer>, 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<Edit>) {
@ -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::<String>();
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(

View file

@ -13,8 +13,9 @@ use std::{
pub struct TabMap(Mutex<Snapshot>);
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(

View file

@ -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<InputEdit>)>,
}
#[derive(Clone)]
pub struct Config {
pub wrap_width: f32,
pub font_family: FamilyId,
pub font_size: f32,
}
pub struct WrapMap {
state: Mutex<State>,
edits_tx: channel::Sender<(InputSnapshot, Vec<InputEdit>)>,
@ -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<f32>,
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<f32>) {
todo!()
}
}
struct BackgroundWrapper {
config: Config,
settings: Settings,
wrap_width: Option<f32>,
font_cache: Arc<FontCache>,
font_system: Arc<dyn FontSystem>,
snapshot: Snapshot,
@ -289,12 +295,14 @@ struct BackgroundWrapper {
impl BackgroundWrapper {
fn new(
snapshot: Snapshot,
config: Config,
settings: Settings,
wrap_width: Option<f32>,
font_cache: Arc<FontCache>,
font_system: Arc<dyn FontSystem>,
) -> 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::<String>(),
" 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

View file

@ -17,8 +17,9 @@ mod util;
pub mod workspace;
pub mod worktree;
pub use settings::Settings;
pub struct AppState {
pub settings: postage::watch::Receiver<settings::Settings>,
pub settings: postage::watch::Receiver<Settings>,
pub languages: std::sync::Arc<language::LanguageRegistry>,
pub rpc_router: std::sync::Arc<ForegroundRouter>,
pub rpc: rpc::Client,

View file

@ -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 {