1533 lines · 56.1 KB · Rust
RawDownload
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,} #[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);                        }                    }                     if line.is_empty() {                        break;                    }                     let mut prev_boundary_ix = 0;                    for boundary in line_wrapper.wrap_line(&line_fragments, wrap_width) {                        let wrapped = &line[prev_boundary_ix..boundary.ix];

Lines 1–500 of 1533.