use super::{ fold_map::FoldBufferRows, tab_map::{self, TabEdit, TabPoint, TabSnapshot}, Highlights,};use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};use language::{Chunk, Point};use lazy_static::lazy_static;use multi_buffer::MultiBufferSnapshot;use smol::future::yield_now;use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};use sum_tree::{Bias, Cursor, SumTree};use text::Patch; pub use super::tab_map::TextSummary;pub type WrapEdit = text::Edit<u32>; /// Handles soft wrapping of text.////// See the [`display_map` module documentation](crate::display_map) for more information.pub struct WrapMap { snapshot: WrapSnapshot, pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>, interpolated_edits: Patch<u32>, edits_since_sync: Patch<u32>, wrap_width: Option<Pixels>, background_task: Option<Task<()>>, font_with_size: (Font, Pixels),} #[derive(Clone)]pub struct WrapSnapshot { tab_snapshot: TabSnapshot, transforms: SumTree<Transform>, interpolated: bool,} #[derive(Clone, Debug, Default, Eq, PartialEq)]struct Transform { summary: TransformSummary, display_text: Option<&'static str>,} #[derive(Clone, Debug, Default, Eq, PartialEq)]struct TransformSummary { input: TextSummary, output: TextSummary,} #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct WrapPoint(pub Point); pub struct WrapChunks<'a> { input_chunks: tab_map::TabChunks<'a>, input_chunk: Chunk<'a>, output_position: WrapPoint, max_output_row: u32, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,} #[derive(Clone)]pub struct WrapBufferRows<'a> { input_buffer_rows: FoldBufferRows<'a>, input_buffer_row: Option<u32>, output_row: u32, soft_wrapped: bool, max_output_row: u32, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,} impl WrapMap { pub fn new( tab_snapshot: TabSnapshot, font: Font, font_size: Pixels, wrap_width: Option<Pixels>, cx: &mut AppContext, ) -> (Model<Self>, WrapSnapshot) { let handle = cx.new_model(|cx| { let mut this = Self { font_with_size: (font, font_size), wrap_width: None, pending_edits: Default::default(), interpolated_edits: Default::default(), edits_since_sync: Default::default(), snapshot: WrapSnapshot::new(tab_snapshot), background_task: None, }; this.set_wrap_width(wrap_width, cx); mem::take(&mut this.edits_since_sync); this }); let snapshot = handle.read(cx).snapshot.clone(); (handle, snapshot) } #[cfg(test)] pub fn is_rewrapping(&self) -> bool { self.background_task.is_some() } pub fn sync( &mut self, tab_snapshot: TabSnapshot, edits: Vec<TabEdit>, cx: &mut ModelContext<Self>, ) -> (WrapSnapshot, Patch<u32>) { if self.wrap_width.is_some() { self.pending_edits.push_back((tab_snapshot, edits)); self.flush_edits(cx); } else { self.edits_since_sync = self .edits_since_sync .compose(&self.snapshot.interpolate(tab_snapshot, &edits)); self.snapshot.interpolated = false; } (self.snapshot.clone(), mem::take(&mut self.edits_since_sync)) } pub fn set_font_with_size( &mut self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>, ) -> bool { let font_with_size = (font, font_size); if font_with_size != self.font_with_size { self.font_with_size = font_with_size; self.rewrap(cx); true } else { false } } pub fn set_wrap_width( &mut self, wrap_width: Option<Pixels>, cx: &mut ModelContext<Self>, ) -> bool { if wrap_width == self.wrap_width { return false; } self.wrap_width = wrap_width; self.rewrap(cx); true } fn rewrap(&mut self, cx: &mut ModelContext<Self>) { self.background_task.take(); self.interpolated_edits.clear(); self.pending_edits.clear(); if let Some(wrap_width) = self.wrap_width { let mut new_snapshot = self.snapshot.clone(); let text_system = cx.text_system().clone(); let (font, font_size) = self.font_with_size.clone(); let task = cx.background_executor().spawn(async move { let mut line_wrapper = text_system.line_wrapper(font, font_size); let tab_snapshot = new_snapshot.tab_snapshot.clone(); let range = TabPoint::zero()..tab_snapshot.max_point(); let edits = new_snapshot .update( tab_snapshot, &[TabEdit { old: range.clone(), new: range.clone(), }], wrap_width, &mut line_wrapper, ) .await; (new_snapshot, edits) }); match cx .background_executor() .block_with_timeout(Duration::from_millis(5), task) { Ok((snapshot, edits)) => { self.snapshot = snapshot; self.edits_since_sync = self.edits_since_sync.compose(&edits); } Err(wrap_task) => { self.background_task = Some(cx.spawn(|this, mut cx| async move { let (snapshot, edits) = wrap_task.await; this.update(&mut cx, |this, cx| { this.snapshot = snapshot; this.edits_since_sync = this .edits_since_sync .compose(mem::take(&mut this.interpolated_edits).invert()) .compose(&edits); this.background_task = None; this.flush_edits(cx); cx.notify(); }) .ok(); })); } } } else { let old_rows = self.snapshot.transforms.summary().output.lines.row + 1; self.snapshot.transforms = SumTree::new(); let summary = self.snapshot.tab_snapshot.text_summary(); if !summary.lines.is_zero() { self.snapshot .transforms .push(Transform::isomorphic(summary), &()); } let new_rows = self.snapshot.transforms.summary().output.lines.row + 1; self.snapshot.interpolated = false; self.edits_since_sync = self.edits_since_sync.compose(&Patch::new(vec![WrapEdit { old: 0..old_rows, new: 0..new_rows, }])); } } fn flush_edits(&mut self, cx: &mut ModelContext<Self>) { if !self.snapshot.interpolated { let mut to_remove_len = 0; for (tab_snapshot, _) in &self.pending_edits { if tab_snapshot.version <= self.snapshot.tab_snapshot.version { to_remove_len += 1; } else { break; } } self.pending_edits.drain(..to_remove_len); } if self.pending_edits.is_empty() { return; } if let Some(wrap_width) = self.wrap_width { if self.background_task.is_none() { let pending_edits = self.pending_edits.clone(); let mut snapshot = self.snapshot.clone(); let text_system = cx.text_system().clone(); let (font, font_size) = self.font_with_size.clone(); let update_task = cx.background_executor().spawn(async move { let mut edits = Patch::default(); let mut line_wrapper = text_system.line_wrapper(font, font_size); for (tab_snapshot, tab_edits) in pending_edits { let wrap_edits = snapshot .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper) .await; edits = edits.compose(&wrap_edits); } (snapshot, edits) }); match cx .background_executor() .block_with_timeout(Duration::from_millis(1), update_task) { Ok((snapshot, output_edits)) => { self.snapshot = snapshot; self.edits_since_sync = self.edits_since_sync.compose(&output_edits); } Err(update_task) => { self.background_task = Some(cx.spawn(|this, mut cx| async move { let (snapshot, edits) = update_task.await; this.update(&mut cx, |this, cx| { this.snapshot = snapshot; this.edits_since_sync = this .edits_since_sync .compose(mem::take(&mut this.interpolated_edits).invert()) .compose(&edits); this.background_task = None; this.flush_edits(cx); cx.notify(); }) .ok(); })); } } } } let was_interpolated = self.snapshot.interpolated; let mut to_remove_len = 0; for (tab_snapshot, edits) in &self.pending_edits { if tab_snapshot.version <= self.snapshot.tab_snapshot.version { to_remove_len += 1; } else { let interpolated_edits = self.snapshot.interpolate(tab_snapshot.clone(), edits); self.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits); self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits); } } if !was_interpolated { self.pending_edits.drain(..to_remove_len); } }} impl WrapSnapshot { fn new(tab_snapshot: TabSnapshot) -> Self { let mut transforms = SumTree::new(); let extent = tab_snapshot.text_summary(); if !extent.lines.is_zero() { transforms.push(Transform::isomorphic(extent), &()); } Self { transforms, tab_snapshot, interpolated: true, } } pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot { self.tab_snapshot.buffer_snapshot() } fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> { let mut new_transforms; if tab_edits.is_empty() { new_transforms = self.transforms.clone(); } else { let mut old_cursor = self.transforms.cursor::<TabPoint>(); let mut tab_edits_iter = tab_edits.iter().peekable(); new_transforms = old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &()); while let Some(edit) = tab_edits_iter.next() { if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) { let summary = new_tab_snapshot.text_summary_for_range( TabPoint::from(new_transforms.summary().input.lines)..edit.new.start, ); new_transforms.push_or_extend(Transform::isomorphic(summary)); } if !edit.new.is_empty() { new_transforms.push_or_extend(Transform::isomorphic( new_tab_snapshot.text_summary_for_range(edit.new.clone()), )); } old_cursor.seek_forward(&edit.old.end, Bias::Right, &()); if let Some(next_edit) = tab_edits_iter.peek() { if next_edit.old.start > old_cursor.end(&()) { if old_cursor.end(&()) > edit.old.end { let summary = self .tab_snapshot .text_summary_for_range(edit.old.end..old_cursor.end(&())); new_transforms.push_or_extend(Transform::isomorphic(summary)); } old_cursor.next(&()); new_transforms.append( old_cursor.slice(&next_edit.old.start, Bias::Right, &()), &(), ); } } else { if old_cursor.end(&()) > edit.old.end { let summary = self .tab_snapshot .text_summary_for_range(edit.old.end..old_cursor.end(&())); new_transforms.push_or_extend(Transform::isomorphic(summary)); } old_cursor.next(&()); new_transforms.append(old_cursor.suffix(&()), &()); } } } let old_snapshot = mem::replace( self, WrapSnapshot { tab_snapshot: new_tab_snapshot, transforms: new_transforms, interpolated: true, }, ); self.check_invariants(); old_snapshot.compute_edits(tab_edits, self) } async fn update( &mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit], wrap_width: Pixels, line_wrapper: &mut LineWrapper, ) -> Patch<u32> { #[derive(Debug)] struct RowEdit { old_rows: Range<u32>, new_rows: Range<u32>, } let mut tab_edits_iter = tab_edits.iter().peekable(); let mut row_edits = Vec::new(); while let Some(edit) = tab_edits_iter.next() { let mut row_edit = RowEdit { old_rows: edit.old.start.row()..edit.old.end.row() + 1, new_rows: edit.new.start.row()..edit.new.end.row() + 1, }; while let Some(next_edit) = tab_edits_iter.peek() { if next_edit.old.start.row() <= row_edit.old_rows.end { row_edit.old_rows.end = next_edit.old.end.row() + 1; row_edit.new_rows.end = next_edit.new.end.row() + 1; tab_edits_iter.next(); } else { break; } } row_edits.push(row_edit); } let mut new_transforms; if row_edits.is_empty() { new_transforms = self.transforms.clone(); } else { let mut row_edits = row_edits.into_iter().peekable(); let mut old_cursor = self.transforms.cursor::<TabPoint>(); new_transforms = old_cursor.slice( &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0), Bias::Right, &(), ); while let Some(edit) = row_edits.next() { if edit.new_rows.start > new_transforms.summary().input.lines.row { let summary = new_tab_snapshot.text_summary_for_range( TabPoint(new_transforms.summary().input.lines) ..TabPoint::new(edit.new_rows.start, 0), ); new_transforms.push_or_extend(Transform::isomorphic(summary)); } let mut line = String::new(); let mut remaining = None; let mut chunks = new_tab_snapshot.chunks( TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(), false, Highlights::default(), ); let mut edit_transforms = Vec::<Transform>::new(); for _ in edit.new_rows.start..edit.new_rows.end { while let Some(chunk) = remaining.take().or_else(|| chunks.next().map(|c| c.text)) { if let Some(ix) = chunk.find('\n') { line.push_str(&chunk[..ix + 1]); remaining = Some(&chunk[ix + 1..]); break; } else { line.push_str(chunk) } } if line.is_empty() { break; } let mut prev_boundary_ix = 0; for boundary in line_wrapper.wrap_line(&line, wrap_width) { let wrapped = &line[prev_boundary_ix..boundary.ix]; push_isomorphic(&mut edit_transforms, TextSummary::from(wrapped)); edit_transforms.push(Transform::wrap(boundary.next_indent)); prev_boundary_ix = boundary.ix; } if prev_boundary_ix < line.len() { push_isomorphic( &mut edit_transforms, TextSummary::from(&line[prev_boundary_ix..]), ); } line.clear(); yield_now().await; } let mut edit_transforms = edit_transforms.into_iter(); if let Some(transform) = edit_transforms.next() { new_transforms.push_or_extend(transform); } new_transforms.extend(edit_transforms, &()); old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &()); if let Some(next_edit) = row_edits.peek() { if next_edit.old_rows.start > old_cursor.end(&()).row() { if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) { let summary = self.tab_snapshot.text_summary_for_range( TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()), );Lines 1–500 of 1356. Load more
use super::{ fold_map::FoldBufferRows, tab_map::{self, TabEdit, TabPoint, TabSnapshot}, Highlights, }; use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task}; use language::{Chunk, Point}; use lazy_static::lazy_static; use multi_buffer::MultiBufferSnapshot; use smol::future::yield_now; use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration}; use sum_tree::{Bias, Cursor, SumTree}; use text::Patch; pub use super::tab_map::TextSummary; pub type WrapEdit = text::Edit<u32>; /// Handles soft wrapping of text. /// /// See the [`display_map` module documentation](crate::display_map) for more information. pub struct WrapMap { snapshot: WrapSnapshot, pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>, interpolated_edits: Patch<u32>, edits_since_sync: Patch<u32>, wrap_width: Option<Pixels>, background_task: Option<Task<()>>, font_with_size: (Font, Pixels), } #[derive(Clone)] pub struct WrapSnapshot { tab_snapshot: TabSnapshot, transforms: SumTree<Transform>, interpolated: bool, } #[derive(Clone, Debug, Default, Eq, PartialEq)] struct Transform { summary: TransformSummary, display_text: Option<&'static str>, } #[derive(Clone, Debug, Default, Eq, PartialEq)] struct TransformSummary { input: TextSummary, output: TextSummary, } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct WrapPoint(pub Point); pub struct WrapChunks<'a> { input_chunks: tab_map::TabChunks<'a>, input_chunk: Chunk<'a>, output_position: WrapPoint, max_output_row: u32, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>, } #[derive(Clone)] pub struct WrapBufferRows<'a> { input_buffer_rows: FoldBufferRows<'a>, input_buffer_row: Option<u32>, output_row: u32, soft_wrapped: bool, max_output_row: u32, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>, } impl WrapMap { pub fn new( tab_snapshot: TabSnapshot, font: Font, font_size: Pixels, wrap_width: Option<Pixels>, cx: &mut AppContext, ) -> (Model<Self>, WrapSnapshot) { let handle = cx.new_model(|cx| { let mut this = Self { font_with_size: (font, font_size), wrap_width: None, pending_edits: Default::default(), interpolated_edits: Default::default(), edits_since_sync: Default::default(), snapshot: WrapSnapshot::new(tab_snapshot), background_task: None, }; this.set_wrap_width(wrap_width, cx); mem::take(&mut this.edits_since_sync); this }); let snapshot = handle.read(cx).snapshot.clone(); (handle, snapshot) } #[cfg(test)] pub fn is_rewrapping(&self) -> bool { self.background_task.is_some() } pub fn sync( &mut self, tab_snapshot: TabSnapshot, edits: Vec<TabEdit>, cx: &mut ModelContext<Self>, ) -> (WrapSnapshot, Patch<u32>) { if self.wrap_width.is_some() { self.pending_edits.push_back((tab_snapshot, edits)); self.flush_edits(cx); } else { self.edits_since_sync = self .edits_since_sync .compose(&self.snapshot.interpolate(tab_snapshot, &edits)); self.snapshot.interpolated = false; } (self.snapshot.clone(), mem::take(&mut self.edits_since_sync)) } pub fn set_font_with_size( &mut self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>, ) -> bool { let font_with_size = (font, font_size); if font_with_size != self.font_with_size { self.font_with_size = font_with_size; self.rewrap(cx); true } else { false } } pub fn set_wrap_width( &mut self, wrap_width: Option<Pixels>, cx: &mut ModelContext<Self>, ) -> bool { if wrap_width == self.wrap_width { return false; } self.wrap_width = wrap_width; self.rewrap(cx); true } fn rewrap(&mut self, cx: &mut ModelContext<Self>) { self.background_task.take(); self.interpolated_edits.clear(); self.pending_edits.clear(); if let Some(wrap_width) = self.wrap_width { let mut new_snapshot = self.snapshot.clone(); let text_system = cx.text_system().clone(); let (font, font_size) = self.font_with_size.clone(); let task = cx.background_executor().spawn(async move { let mut line_wrapper = text_system.line_wrapper(font, font_size); let tab_snapshot = new_snapshot.tab_snapshot.clone(); let range = TabPoint::zero()..tab_snapshot.max_point(); let edits = new_snapshot .update( tab_snapshot, &[TabEdit { old: range.clone(), new: range.clone(), }], wrap_width, &mut line_wrapper, ) .await; (new_snapshot, edits) }); match cx .background_executor() .block_with_timeout(Duration::from_millis(5), task) { Ok((snapshot, edits)) => { self.snapshot = snapshot; self.edits_since_sync = self.edits_since_sync.compose(&edits); } Err(wrap_task) => { self.background_task = Some(cx.spawn(|this, mut cx| async move { let (snapshot, edits) = wrap_task.await; this.update(&mut cx, |this, cx| { this.snapshot = snapshot; this.edits_since_sync = this .edits_since_sync .compose(mem::take(&mut this.interpolated_edits).invert()) .compose(&edits); this.background_task = None; this.flush_edits(cx); cx.notify(); }) .ok(); })); } } } else { let old_rows = self.snapshot.transforms.summary().output.lines.row + 1; self.snapshot.transforms = SumTree::new(); let summary = self.snapshot.tab_snapshot.text_summary(); if !summary.lines.is_zero() { self.snapshot .transforms .push(Transform::isomorphic(summary), &()); } let new_rows = self.snapshot.transforms.summary().output.lines.row + 1; self.snapshot.interpolated = false; self.edits_since_sync = self.edits_since_sync.compose(&Patch::new(vec![WrapEdit { old: 0..old_rows, new: 0..new_rows, }])); } } fn flush_edits(&mut self, cx: &mut ModelContext<Self>) { if !self.snapshot.interpolated { let mut to_remove_len = 0; for (tab_snapshot, _) in &self.pending_edits { if tab_snapshot.version <= self.snapshot.tab_snapshot.version { to_remove_len += 1; } else { break; } } self.pending_edits.drain(..to_remove_len); } if self.pending_edits.is_empty() { return; } if let Some(wrap_width) = self.wrap_width { if self.background_task.is_none() { let pending_edits = self.pending_edits.clone(); let mut snapshot = self.snapshot.clone(); let text_system = cx.text_system().clone(); let (font, font_size) = self.font_with_size.clone(); let update_task = cx.background_executor().spawn(async move { let mut edits = Patch::default(); let mut line_wrapper = text_system.line_wrapper(font, font_size); for (tab_snapshot, tab_edits) in pending_edits { let wrap_edits = snapshot .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper) .await; edits = edits.compose(&wrap_edits); } (snapshot, edits) }); match cx .background_executor() .block_with_timeout(Duration::from_millis(1), update_task) { Ok((snapshot, output_edits)) => { self.snapshot = snapshot; self.edits_since_sync = self.edits_since_sync.compose(&output_edits); } Err(update_task) => { self.background_task = Some(cx.spawn(|this, mut cx| async move { let (snapshot, edits) = update_task.await; this.update(&mut cx, |this, cx| { this.snapshot = snapshot; this.edits_since_sync = this .edits_since_sync .compose(mem::take(&mut this.interpolated_edits).invert()) .compose(&edits); this.background_task = None; this.flush_edits(cx); cx.notify(); }) .ok(); })); } } } } let was_interpolated = self.snapshot.interpolated; let mut to_remove_len = 0; for (tab_snapshot, edits) in &self.pending_edits { if tab_snapshot.version <= self.snapshot.tab_snapshot.version { to_remove_len += 1; } else { let interpolated_edits = self.snapshot.interpolate(tab_snapshot.clone(), edits); self.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits); self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits); } } if !was_interpolated { self.pending_edits.drain(..to_remove_len); } } } impl WrapSnapshot { fn new(tab_snapshot: TabSnapshot) -> Self { let mut transforms = SumTree::new(); let extent = tab_snapshot.text_summary(); if !extent.lines.is_zero() { transforms.push(Transform::isomorphic(extent), &()); } Self { transforms, tab_snapshot, interpolated: true, } } pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot { self.tab_snapshot.buffer_snapshot() } fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> { let mut new_transforms; if tab_edits.is_empty() { new_transforms = self.transforms.clone(); } else { let mut old_cursor = self.transforms.cursor::<TabPoint>(); let mut tab_edits_iter = tab_edits.iter().peekable(); new_transforms = old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &()); while let Some(edit) = tab_edits_iter.next() { if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) { let summary = new_tab_snapshot.text_summary_for_range( TabPoint::from(new_transforms.summary().input.lines)..edit.new.start, ); new_transforms.push_or_extend(Transform::isomorphic(summary)); } if !edit.new.is_empty() { new_transforms.push_or_extend(Transform::isomorphic( new_tab_snapshot.text_summary_for_range(edit.new.clone()), )); } old_cursor.seek_forward(&edit.old.end, Bias::Right, &()); if let Some(next_edit) = tab_edits_iter.peek() { if next_edit.old.start > old_cursor.end(&()) { if old_cursor.end(&()) > edit.old.end { let summary = self .tab_snapshot .text_summary_for_range(edit.old.end..old_cursor.end(&())); new_transforms.push_or_extend(Transform::isomorphic(summary)); } old_cursor.next(&()); new_transforms.append( old_cursor.slice(&next_edit.old.start, Bias::Right, &()), &(), ); } } else { if old_cursor.end(&()) > edit.old.end { let summary = self .tab_snapshot .text_summary_for_range(edit.old.end..old_cursor.end(&())); new_transforms.push_or_extend(Transform::isomorphic(summary)); } old_cursor.next(&()); new_transforms.append(old_cursor.suffix(&()), &()); } } } let old_snapshot = mem::replace( self, WrapSnapshot { tab_snapshot: new_tab_snapshot, transforms: new_transforms, interpolated: true, }, ); self.check_invariants(); old_snapshot.compute_edits(tab_edits, self) } async fn update( &mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit], wrap_width: Pixels, line_wrapper: &mut LineWrapper, ) -> Patch<u32> { #[derive(Debug)] struct RowEdit { old_rows: Range<u32>, new_rows: Range<u32>, } let mut tab_edits_iter = tab_edits.iter().peekable(); let mut row_edits = Vec::new(); while let Some(edit) = tab_edits_iter.next() { let mut row_edit = RowEdit { old_rows: edit.old.start.row()..edit.old.end.row() + 1, new_rows: edit.new.start.row()..edit.new.end.row() + 1, }; while let Some(next_edit) = tab_edits_iter.peek() { if next_edit.old.start.row() <= row_edit.old_rows.end { row_edit.old_rows.end = next_edit.old.end.row() + 1; row_edit.new_rows.end = next_edit.new.end.row() + 1; tab_edits_iter.next(); } else { break; } } row_edits.push(row_edit); } let mut new_transforms; if row_edits.is_empty() { new_transforms = self.transforms.clone(); } else { let mut row_edits = row_edits.into_iter().peekable(); let mut old_cursor = self.transforms.cursor::<TabPoint>(); new_transforms = old_cursor.slice( &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0), Bias::Right, &(), ); while let Some(edit) = row_edits.next() { if edit.new_rows.start > new_transforms.summary().input.lines.row { let summary = new_tab_snapshot.text_summary_for_range( TabPoint(new_transforms.summary().input.lines) ..TabPoint::new(edit.new_rows.start, 0), ); new_transforms.push_or_extend(Transform::isomorphic(summary)); } let mut line = String::new(); let mut remaining = None; let mut chunks = new_tab_snapshot.chunks( TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(), false, Highlights::default(), ); let mut edit_transforms = Vec::<Transform>::new(); for _ in edit.new_rows.start..edit.new_rows.end { while let Some(chunk) = remaining.take().or_else(|| chunks.next().map(|c| c.text)) { if let Some(ix) = chunk.find('\n') { line.push_str(&chunk[..ix + 1]); remaining = Some(&chunk[ix + 1..]); break; } else { line.push_str(chunk) } } if line.is_empty() { break; } let mut prev_boundary_ix = 0; for boundary in line_wrapper.wrap_line(&line, wrap_width) { let wrapped = &line[prev_boundary_ix..boundary.ix]; push_isomorphic(&mut edit_transforms, TextSummary::from(wrapped)); edit_transforms.push(Transform::wrap(boundary.next_indent)); prev_boundary_ix = boundary.ix; } if prev_boundary_ix < line.len() { push_isomorphic( &mut edit_transforms, TextSummary::from(&line[prev_boundary_ix..]), ); } line.clear(); yield_now().await; } let mut edit_transforms = edit_transforms.into_iter(); if let Some(transform) = edit_transforms.next() { new_transforms.push_or_extend(transform); } new_transforms.extend(edit_transforms, &()); old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &()); if let Some(next_edit) = row_edits.peek() { if next_edit.old_rows.start > old_cursor.end(&()).row() { if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) { let summary = self.tab_snapshot.text_summary_for_range( TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()), );