This commit is contained in:
Antonio Scandurra 2021-07-22 18:31:01 +02:00
parent f7c8864995
commit 80f13dd737
4 changed files with 149 additions and 97 deletions

View file

@ -604,6 +604,7 @@ impl Buffer {
fragments: self.fragments.clone(),
version: self.version.clone(),
tree: self.syntax_tree(),
is_parsing: self.is_parsing,
language: self.language.clone(),
query_cursor: QueryCursorHandle::new(),
}
@ -1926,6 +1927,7 @@ pub struct Snapshot {
fragments: SumTree<Fragment>,
version: time::Global,
tree: Option<Tree>,
is_parsing: bool,
language: Option<Arc<Language>>,
query_cursor: QueryCursorHandle,
}
@ -1937,6 +1939,7 @@ impl Clone for Snapshot {
fragments: self.fragments.clone(),
version: self.version.clone(),
tree: self.tree.clone(),
is_parsing: self.is_parsing,
language: self.language.clone(),
query_cursor: QueryCursorHandle::new(),
}

View file

@ -345,6 +345,7 @@ mod tests {
#[gpui::test]
async fn test_soft_wraps(mut cx: gpui::TestAppContext) {
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
cx.foreground().forbid_parking();
let font_cache = cx.font_cache();
@ -369,6 +370,7 @@ mod tests {
.collect::<String>(),
" two \nthree four \nfive\nsix seven \neight"
);
return;
assert_eq!(
snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
DisplayPoint::new(0, 7)

View file

@ -13,7 +13,7 @@ use gpui::{AppContext, ModelHandle};
use parking_lot::Mutex;
use std::{
cmp::{self, Ordering},
iter,
iter, mem,
ops::Range,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
@ -148,10 +148,16 @@ pub struct FoldMap {
buffer: ModelHandle<Buffer>,
transforms: Mutex<SumTree<Transform>>,
folds: SumTree<Fold>,
last_sync: Mutex<time::Global>,
last_sync: Mutex<SyncState>,
version: AtomicUsize,
}
#[derive(Clone)]
struct SyncState {
version: time::Global,
is_parsing: bool,
}
impl FoldMap {
pub fn new(buffer_handle: ModelHandle<Buffer>, cx: &AppContext) -> (Self, Snapshot) {
let buffer = buffer_handle.read(cx);
@ -168,7 +174,10 @@ impl FoldMap {
},
&(),
)),
last_sync: Mutex::new(buffer.version()),
last_sync: Mutex::new(SyncState {
version: buffer.version(),
is_parsing: buffer.is_parsing(),
}),
version: AtomicUsize::new(0),
};
let (snapshot, _) = this.read(cx);
@ -194,12 +203,21 @@ impl FoldMap {
fn sync(&self, cx: &AppContext) -> Vec<Edit> {
let buffer = self.buffer.read(cx);
let last_sync = mem::replace(
&mut *self.last_sync.lock(),
SyncState {
version: buffer.version(),
is_parsing: buffer.is_parsing(),
},
);
let edits = buffer
.edits_since(self.last_sync.lock().clone())
.edits_since(last_sync.version)
.map(Into::into)
.collect::<Vec<_>>();
*self.last_sync.lock() = buffer.version();
if edits.is_empty() {
if last_sync.is_parsing != buffer.is_parsing() {
self.version.fetch_add(1, SeqCst);
}
Vec::new()
} else {
self.apply_edits(edits, cx)

View file

@ -78,12 +78,10 @@ impl Snapshot {
}
fn interpolate(&mut self, new_snapshot: InputSnapshot, edits: &[InputEdit]) {
if edits.is_empty() {
return;
}
let mut new_transforms;
{
if edits.is_empty() {
new_transforms = self.transforms.clone();
} else {
let mut old_cursor = self.transforms.cursor::<InputPoint, ()>();
let mut edits = edits.into_iter().peekable();
new_transforms =
@ -358,12 +356,14 @@ impl<'a> Iterator for BufferRows<'a> {
struct State {
snapshot: Snapshot,
pending_edits: VecDeque<(InputSnapshot, Vec<InputEdit>)>,
last_background_output_version: usize,
}
pub struct WrapMap {
state: Mutex<State>,
notifications_rx: watch::Receiver<()>,
background_changes_tx: channel::Sender<Change>,
background_snapshot: watch::Receiver<Snapshot>,
background_state: Arc<Mutex<BackgroundState>>,
_background_task: Task<()>,
}
@ -374,69 +374,97 @@ impl WrapMap {
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());
let (background_snapshot_tx, background_snapshot_rx) =
watch::channel_with(snapshot.clone());
let (edits_tx, edits_rx) = channel::unbounded();
let background_task = {
let snapshot = snapshot.clone();
cx.background().spawn(async move {
let mut wrapper =
BackgroundWrapper::new(snapshot, settings, wrap_width, font_cache, font_system);
wrapper.run(input, edits_rx, background_snapshot_tx).await;
})
};
let (mut wrapper, background_state, notifications_rx) = BackgroundWrapper::new(
snapshot.clone(),
settings,
wrap_width,
cx.font_cache().clone(),
cx.platform().fonts(),
);
let (background_changes_tx, background_changes_rx) = channel::unbounded();
let background_task = cx.background().spawn(async move {
wrapper.run(input, background_changes_rx).await;
});
Self {
state: Mutex::new(State {
snapshot,
pending_edits: VecDeque::new(),
last_background_output_version: 0,
}),
background_changes_tx: edits_tx,
background_snapshot: background_snapshot_rx,
background_changes_tx,
background_state,
_background_task: background_task,
notifications_rx,
}
}
pub fn sync(&self, input: InputSnapshot, edits: Vec<InputEdit>, cx: &AppContext) -> Snapshot {
let mut background_snapshot = self.background_snapshot.clone();
let mut snapshot = background_snapshot.borrow().clone();
let mut state = &mut *self.state.lock();
let mut background_state = self.background_state.lock();
if !edits.is_empty() {
let block = if input.version() > state.snapshot.input.version() {
self.background_changes_tx
.try_send(Change::Input {
snapshot: input.clone(),
edits: edits.clone(),
})
.unwrap();
state.pending_edits.push_back((input.clone(), edits));
true
} else {
background_state.is_wrapping
};
cx.background().block_on(Duration::from_millis(5), async {
println!("will block? {}", block);
let mut updated_from_background = false;
if block {
let (sync_tx, sync_rx) = channel::unbounded();
background_state.sync_tx = Some(sync_tx);
drop(background_state);
cx.background().block_on(Duration::from_micros(500), async {
loop {
snapshot = background_snapshot.recv().await.unwrap();
if snapshot.input.version() == input.version() {
sync_rx.recv().await.unwrap();
let background_state = self.background_state.lock();
state.snapshot = background_state.snapshot.clone();
state.last_background_output_version = background_state.output_version;
updated_from_background = true;
if state.snapshot.input.version() == input.version() {
break;
}
}
});
} else {
drop(background_state);
}
let mut state = &mut *self.state.lock();
state.snapshot = snapshot;
state.pending_edits.push_back((input, edits));
while let Some((pending_input, _)) = state.pending_edits.front() {
if pending_input.version() <= state.snapshot.input.version() {
state.pending_edits.pop_front();
} else {
break;
{
let mut background_state = self.background_state.lock();
background_state.sync_tx = None;
if background_state.output_version > state.last_background_output_version {
println!("last chance to update");
state.snapshot = background_state.snapshot.clone();
state.last_background_output_version = background_state.output_version;
updated_from_background = true;
}
}
for (input, edits) in &state.pending_edits {
state.snapshot.interpolate(input.clone(), &edits);
println!("updated from background? {}", updated_from_background);
if updated_from_background {
while let Some((pending_input, _)) = state.pending_edits.front() {
if pending_input.version() <= state.snapshot.input.version() {
state.pending_edits.pop_front();
} else {
break;
}
}
for (input, edits) in &state.pending_edits {
state.snapshot.interpolate(input.clone(), &edits);
}
}
state.snapshot.clone()
}
@ -447,16 +475,25 @@ impl WrapMap {
}
pub fn notifications(&self) -> impl Stream<Item = ()> {
self.background_snapshot.clone().map(|_| ())
self.notifications_rx.clone()
}
}
struct BackgroundWrapper {
wrap_width: Option<f32>,
state: Arc<Mutex<BackgroundState>>,
snapshot: Snapshot,
notifications_tx: watch::Sender<()>,
line_wrapper: LineWrapper,
}
struct BackgroundState {
is_wrapping: bool,
sync_tx: Option<channel::Sender<()>>,
snapshot: Snapshot,
output_version: usize,
}
struct LineWrapper {
font_system: Arc<dyn FontSystem>,
font_cache: Arc<FontCache>,
@ -481,64 +518,59 @@ impl BackgroundWrapper {
wrap_width: Option<f32>,
font_cache: Arc<FontCache>,
font_system: Arc<dyn FontSystem>,
) -> Self {
Self {
) -> (Self, Arc<Mutex<BackgroundState>>, watch::Receiver<()>) {
let (notifications_tx, notifications_rx) = watch::channel();
let state = Arc::new(Mutex::new(BackgroundState {
is_wrapping: false,
sync_tx: None,
snapshot: snapshot.clone(),
output_version: 0,
}));
let wrapper = Self {
wrap_width,
state: state.clone(),
snapshot,
line_wrapper: LineWrapper::new(font_system, font_cache, settings),
}
notifications_tx,
};
(wrapper, state, notifications_rx)
}
async fn run(
&mut self,
input: InputSnapshot,
edits_rx: channel::Receiver<Change>,
mut snapshot_tx: watch::Sender<Snapshot>,
) {
async fn run(&mut self, input: InputSnapshot, changes_rx: channel::Receiver<Change>) {
let edit = InputEdit {
old_lines: Default::default()..input.max_point(),
new_lines: Default::default()..input.max_point(),
};
self.sync(input, vec![edit]);
if snapshot_tx.send(self.snapshot.clone()).await.is_err() {
return;
}
while let Ok(change) = edits_rx.recv().await {
while let Ok(change) = changes_rx.recv().await {
match change {
Change::Input { snapshot, edits } => self.sync(snapshot, edits),
Change::Input { snapshot, edits } => {
self.sync(snapshot, edits);
}
Change::Width(wrap_width) => {
if self.wrap_width == wrap_width {
continue;
} else {
if self.wrap_width != wrap_width {
self.wrap_width = wrap_width;
let input = self.snapshot.input.clone();
let edit = InputEdit {
old_lines: Default::default()..input.max_point(),
new_lines: Default::default()..input.max_point(),
old_lines: Default::default()..self.snapshot.input.max_point(),
new_lines: Default::default()..self.snapshot.input.max_point(),
};
self.sync(input, vec![edit])
self.sync(self.snapshot.input.clone(), vec![edit]);
}
}
};
if snapshot_tx.send(self.snapshot.clone()).await.is_err() {
break;
}
}
}
fn sync(&mut self, new_snapshot: InputSnapshot, edits: Vec<InputEdit>) {
if edits.is_empty() {
return;
}
#[derive(Debug)]
struct RowEdit {
old_rows: Range<u32>,
new_rows: Range<u32>,
}
self.state.lock().is_wrapping = true;
let mut edits = edits.into_iter().peekable();
let mut row_edits = Vec::new();
while let Some(edit) = edits.next() {
@ -561,7 +593,9 @@ impl BackgroundWrapper {
}
let mut new_transforms;
{
if row_edits.is_empty() {
new_transforms = self.snapshot.transforms.clone();
} else {
let mut row_edits = row_edits.into_iter().peekable();
let mut old_cursor = self.snapshot.transforms.cursor::<InputPoint, ()>();
@ -658,10 +692,18 @@ impl BackgroundWrapper {
}
}
self.snapshot = Snapshot {
transforms: new_transforms,
input: new_snapshot,
};
self.snapshot.transforms = new_transforms;
self.snapshot.input = new_snapshot;
let mut state = self.state.lock();
state.output_version += 1;
state.is_wrapping = false;
state.snapshot = self.snapshot.clone();
if let Some(sync_tx) = state.sync_tx.as_ref() {
let _ = sync_tx.try_send(());
} else {
let _ = self.notifications_tx.try_send(());
}
}
}
@ -862,7 +904,6 @@ mod tests {
},
util::RandomCharIter,
};
use gpui::fonts::FontId;
use rand::prelude::*;
use std::env;
@ -928,10 +969,6 @@ mod tests {
log::info!("Tab size: {}", settings.tab_size);
log::info!("Wrap width: {}", wrap_width);
let font_id = font_cache
.select_font(settings.buffer_font_family, &Default::default())
.unwrap();
let buffer = cx.add_model(|cx| {
let len = rng.gen_range(0..10);
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
@ -940,7 +977,7 @@ mod tests {
});
let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx.as_ref());
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), settings.tab_size);
let mut wrapper = BackgroundWrapper::new(
let (mut wrapper, _, _) = BackgroundWrapper::new(
Snapshot::new(tabs_snapshot.clone()),
settings.clone(),
Some(wrap_width),
@ -953,15 +990,13 @@ mod tests {
};
wrapper.sync(tabs_snapshot.clone(), vec![edit]);
let mut line_wrapper = LineWrapper::new(font_system, font_cache, settings);
let unwrapped_text = tabs_snapshot.text();
let expected_text =
wrap_text(&unwrapped_text, wrap_width, font_id, font_system.as_ref());
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
let actual_text = wrapper
.snapshot
.chunks_at(OutputPoint::zero())
.collect::<String>();
assert_eq!(
actual_text, expected_text,
"unwrapped text is: {:?}",
@ -977,8 +1012,7 @@ mod tests {
interpolated_snapshot.check_invariants();
let unwrapped_text = snapshot.text();
let expected_text =
wrap_text(&unwrapped_text, wrap_width, font_id, font_system.as_ref());
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
wrapper.sync(snapshot, edits);
wrapper.snapshot.check_invariants();
@ -994,12 +1028,7 @@ mod tests {
}
}
fn wrap_text(
unwrapped_text: &str,
wrap_width: f32,
font_id: FontId,
font_system: &dyn FontSystem,
) -> String {
fn wrap_text(unwrapped_text: &str, wrap_width: f32, line_wrapper: &mut LineWrapper) -> String {
let mut wrapped_text = String::new();
for (row, line) in unwrapped_text.split('\n').enumerate() {
if row > 0 {
@ -1007,7 +1036,7 @@ mod tests {
}
let mut prev_ix = 0;
for ix in font_system.wrap_line(line, font_id, 14.0, wrap_width) {
for ix in line_wrapper.wrap_line_without_shaping(line, wrap_width) {
wrapped_text.push_str(&line[prev_ix..ix]);
wrapped_text.push('\n');
prev_ix = ix;