mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 13:24:19 +00:00
WIP
This commit is contained in:
parent
f7c8864995
commit
80f13dd737
4 changed files with 149 additions and 97 deletions
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue