1758 lines · 66.1 KB · Rust
RawDownload
use super::{    Highlights,    dimensions::RowDelta,    fold_map::{Chunk, FoldRows},    tab_map::{self, TabEdit, TabPoint, TabSnapshot},}; use futures_lite::future::yield_now;use gpui::{App, AppContext as _, Context, Entity, Font, LineWrapper, Pixels, Task};use language::{LanguageAwareStyling, Point};use multi_buffer::RowInfo;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); const WRAP_YIELD_ROW_INTERVAL: usize = 100; 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,} impl TransformSummary {    fn has_wraps(&self) -> bool {        self.input.lines != self.output.lines    }} #[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<'_> {    #[ztracing::instrument(skip_all)]    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 {    #[ztracing::instrument(skip_all)]    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()    }     #[ztracing::instrument(skip_all)]    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))    }     #[ztracing::instrument(skip_all)]    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        }    }     #[ztracing::instrument(skip_all)]    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    }     #[ztracing::instrument(skip_all)]    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();            let (font, font_size) = self.font_with_size.clone();            let mut line_wrapper = text_system.line_wrapper(font, font_size);            let tab_snapshot = new_snapshot.tab_snapshot.clone();            let total_rows = tab_snapshot.max_point().row() as usize + 1;            let range = TabPoint::zero()..tab_snapshot.max_point();            let tab_edits = [TabEdit {                old: range.clone(),                new: range,            }];             if total_rows < WRAP_YIELD_ROW_INTERVAL {                let edits = gpui::block_on(new_snapshot.update(                    tab_snapshot,                    &tab_edits,                    wrap_width,                    &mut line_wrapper,                ));                self.snapshot = new_snapshot;                self.edits_since_sync = self.edits_since_sync.compose(&edits);            } else {                let task = cx.background_spawn(async move {                    let edits = new_snapshot                        .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)                        .await;                    (new_snapshot, edits)                });                 match cx                    .foreground_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),            }]));        }    }     #[ztracing::instrument(skip_all)]    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 mut 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 mut line_wrapper = text_system.line_wrapper(font, font_size);             if pending_edits.len() == 1                && let Some((_, tab_edits)) = pending_edits.back()                && let [edit] = &**tab_edits                && ((edit.new.end.row().saturating_sub(edit.new.start.row()) + 1) as usize)                    < WRAP_YIELD_ROW_INTERVAL                && let Some((tab_snapshot, tab_edits)) = pending_edits.pop_back()            {                let wrap_edits = gpui::block_on(snapshot.update(                    tab_snapshot,                    &tab_edits,                    wrap_width,                    &mut line_wrapper,                ));                self.snapshot = snapshot;                self.edits_since_sync = self.edits_since_sync.compose(&wrap_edits);            } else {                let update_task = cx.background_spawn(async move {                    let mut edits = Patch::default();                    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                    .foreground_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 {    #[ztracing::instrument(skip_all)]    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,        }    }     #[ztracing::instrument(skip_all)]    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 if !self.transforms.summary().has_wraps()            && !new_tab_snapshot.text_summary().lines.is_zero()        {            // Fast path: without existing wraps, interpolation is a passthrough over the new tab snapshot.            new_transforms = SumTree::default();            new_transforms.push(Transform::isomorphic(new_tab_snapshot.text_summary()), ());        } 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)    }     #[ztracing::instrument(skip_all)]    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 =                        cmp::max(row_edit.old_rows.end, next_edit.old.end.row() + 1);                    row_edit.new_rows.end =                        cmp::max(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();

Lines 1–500 of 1758.