use super::{ Highlights, dimensions::RowDelta, fold_map::{Chunk, FoldRows}, tab_map::{self, TabEdit, TabPoint, TabSnapshot},};use gpui::{App, AppContext as _, Context, Entity, Font, LineWrapper, Pixels, Task};use language::Point;use multi_buffer::{MultiBufferSnapshot, RowInfo};use smol::future::yield_now;use std::{cmp, collections::VecDeque, mem, ops::Range, sync::LazyLock, time::Duration};use sum_tree::{Bias, Cursor, Dimensions, SumTree};use text::Patch; pub use super::tab_map::TextSummary;pub type WrapEdit = text::Edit<WrapRow>;pub type WrapPatch = text::Patch<WrapRow>; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct WrapRow(pub u32); impl_for_row_types! { WrapRow => RowDelta} /// 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: WrapPatch, edits_since_sync: WrapPatch, wrap_width: Option<Pixels>, background_task: Option<Task<()>>, font_with_size: (Font, Pixels),} #[derive(Clone)]pub struct WrapSnapshot { pub(super) tab_snapshot: TabSnapshot, transforms: SumTree<Transform>, interpolated: bool,} impl std::ops::Deref for WrapSnapshot { type Target = TabSnapshot; fn deref(&self) -> &Self::Target { &self.tab_snapshot }} #[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: WrapRow, transforms: Cursor<'a, 'static, Transform, Dimensions<WrapPoint, TabPoint>>, snapshot: &'a WrapSnapshot,} #[derive(Clone)]pub struct WrapRows<'a> { input_buffer_rows: FoldRows<'a>, input_buffer_row: RowInfo, output_row: WrapRow, soft_wrapped: bool, max_output_row: WrapRow, transforms: Cursor<'a, 'static, Transform, Dimensions<WrapPoint, TabPoint>>,} impl WrapRows<'_> { pub(crate) fn seek(&mut self, start_row: WrapRow) { self.transforms .seek(&WrapPoint::new(start_row, 0), Bias::Left); let mut input_row = self.transforms.start().1.row(); if self.transforms.item().is_some_and(|t| t.is_isomorphic()) { input_row += (start_row - self.transforms.start().0.row()).0; } self.soft_wrapped = self.transforms.item().is_some_and(|t| !t.is_isomorphic()); self.input_buffer_rows.seek(input_row); self.input_buffer_row = self.input_buffer_rows.next().unwrap(); self.output_row = start_row; }} impl WrapMap { pub fn new( tab_snapshot: TabSnapshot, font: Font, font_size: Pixels, wrap_width: Option<Pixels>, cx: &mut App, ) -> (Entity<Self>, WrapSnapshot) { let handle = cx.new(|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 Context<Self>, ) -> (WrapSnapshot, WrapPatch) { 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 Context<Self>, ) -> bool { let font_with_size = (font, font_size); if font_with_size == self.font_with_size { false } else { self.font_with_size = font_with_size; self.rewrap(cx); true } } pub fn set_wrap_width(&mut self, wrap_width: Option<Pixels>, cx: &mut Context<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 Context<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_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(async move |this, cx| { let (snapshot, edits) = wrap_task.await; this.update(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::default(); 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: WrapRow(0)..WrapRow(old_rows), new: WrapRow(0)..WrapRow(new_rows), }])); } } fn flush_edits(&mut self, cx: &mut Context<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 && 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_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(async move |this, cx| { let (snapshot, edits) = update_task.await; this.update(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::default(); 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]) -> WrapPatch { 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, ) -> WrapPatch { #[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::with_capacity(tab_edits.len()); 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 line_fragments = Vec::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()) { if let Some(ix) = chunk.text.find('\n') { let (prefix, suffix) = chunk.text.split_at(ix + 1); line_fragments.push(gpui::LineFragment::text(prefix)); line.push_str(prefix); remaining = Some(Chunk { text: suffix, ..chunk }); break; } else { if let Some(width) = chunk.renderer.as_ref().and_then(|r| r.measured_width) { line_fragments .push(gpui::LineFragment::element(width, chunk.text.len())); } else { line_fragments.push(gpui::LineFragment::text(chunk.text)); } line.push_str(chunk.text); } }Lines 1–500 of 1541. Load more
use super::{ Highlights, dimensions::RowDelta, fold_map::{Chunk, FoldRows}, tab_map::{self, TabEdit, TabPoint, TabSnapshot}, }; use gpui::{App, AppContext as _, Context, Entity, Font, LineWrapper, Pixels, Task}; use language::Point; use multi_buffer::{MultiBufferSnapshot, RowInfo}; use smol::future::yield_now; use std::{cmp, collections::VecDeque, mem, ops::Range, sync::LazyLock, time::Duration}; use sum_tree::{Bias, Cursor, Dimensions, SumTree}; use text::Patch; pub use super::tab_map::TextSummary; pub type WrapEdit = text::Edit<WrapRow>; pub type WrapPatch = text::Patch<WrapRow>; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct WrapRow(pub u32); impl_for_row_types! { WrapRow => RowDelta } /// 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: WrapPatch, edits_since_sync: WrapPatch, wrap_width: Option<Pixels>, background_task: Option<Task<()>>, font_with_size: (Font, Pixels), } #[derive(Clone)] pub struct WrapSnapshot { pub(super) tab_snapshot: TabSnapshot, transforms: SumTree<Transform>, interpolated: bool, } impl std::ops::Deref for WrapSnapshot { type Target = TabSnapshot; fn deref(&self) -> &Self::Target { &self.tab_snapshot } } #[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: WrapRow, transforms: Cursor<'a, 'static, Transform, Dimensions<WrapPoint, TabPoint>>, snapshot: &'a WrapSnapshot, } #[derive(Clone)] pub struct WrapRows<'a> { input_buffer_rows: FoldRows<'a>, input_buffer_row: RowInfo, output_row: WrapRow, soft_wrapped: bool, max_output_row: WrapRow, transforms: Cursor<'a, 'static, Transform, Dimensions<WrapPoint, TabPoint>>, } impl WrapRows<'_> { pub(crate) fn seek(&mut self, start_row: WrapRow) { self.transforms .seek(&WrapPoint::new(start_row, 0), Bias::Left); let mut input_row = self.transforms.start().1.row(); if self.transforms.item().is_some_and(|t| t.is_isomorphic()) { input_row += (start_row - self.transforms.start().0.row()).0; } self.soft_wrapped = self.transforms.item().is_some_and(|t| !t.is_isomorphic()); self.input_buffer_rows.seek(input_row); self.input_buffer_row = self.input_buffer_rows.next().unwrap(); self.output_row = start_row; } } impl WrapMap { pub fn new( tab_snapshot: TabSnapshot, font: Font, font_size: Pixels, wrap_width: Option<Pixels>, cx: &mut App, ) -> (Entity<Self>, WrapSnapshot) { let handle = cx.new(|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 Context<Self>, ) -> (WrapSnapshot, WrapPatch) { 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 Context<Self>, ) -> bool { let font_with_size = (font, font_size); if font_with_size == self.font_with_size { false } else { self.font_with_size = font_with_size; self.rewrap(cx); true } } pub fn set_wrap_width(&mut self, wrap_width: Option<Pixels>, cx: &mut Context<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 Context<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_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(async move |this, cx| { let (snapshot, edits) = wrap_task.await; this.update(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::default(); 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: WrapRow(0)..WrapRow(old_rows), new: WrapRow(0)..WrapRow(new_rows), }])); } } fn flush_edits(&mut self, cx: &mut Context<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 && 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_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(async move |this, cx| { let (snapshot, edits) = update_task.await; this.update(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::default(); 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]) -> WrapPatch { 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, ) -> WrapPatch { #[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::with_capacity(tab_edits.len()); 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 line_fragments = Vec::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()) { if let Some(ix) = chunk.text.find('\n') { let (prefix, suffix) = chunk.text.split_at(ix + 1); line_fragments.push(gpui::LineFragment::text(prefix)); line.push_str(prefix); remaining = Some(Chunk { text: suffix, ..chunk }); break; } else { if let Some(width) = chunk.renderer.as_ref().and_then(|r| r.measured_width) { line_fragments .push(gpui::LineFragment::element(width, chunk.text.len())); } else { line_fragments.push(gpui::LineFragment::text(chunk.text)); } line.push_str(chunk.text); } }