814 lines · 28.7 KB · Rust
RawDownload
use super::{    fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot},    Highlights,};use language::{Chunk, Point};use multi_buffer::MultiBufferSnapshot;use std::{cmp, mem, num::NonZeroU32, ops::Range};use sum_tree::Bias; const MAX_EXPANSION_COLUMN: u32 = 256; /// Keeps track of hard tabs in a text buffer.////// See the [`display_map` module documentation](crate::display_map) for more information.pub struct TabMap(TabSnapshot); impl TabMap {    pub fn new(fold_snapshot: FoldSnapshot, tab_size: NonZeroU32) -> (Self, TabSnapshot) {        let snapshot = TabSnapshot {            fold_snapshot,            tab_size,            max_expansion_column: MAX_EXPANSION_COLUMN,            version: 0,        };        (Self(snapshot.clone()), snapshot)    }     #[cfg(test)]    pub fn set_max_expansion_column(&mut self, column: u32) -> TabSnapshot {        self.0.max_expansion_column = column;        self.0.clone()    }     pub fn sync(        &mut self,        fold_snapshot: FoldSnapshot,        mut fold_edits: Vec<FoldEdit>,        tab_size: NonZeroU32,    ) -> (TabSnapshot, Vec<TabEdit>) {        let old_snapshot = &mut self.0;        let mut new_snapshot = TabSnapshot {            fold_snapshot,            tab_size,            max_expansion_column: old_snapshot.max_expansion_column,            version: old_snapshot.version,        };         if old_snapshot.fold_snapshot.version != new_snapshot.fold_snapshot.version {            new_snapshot.version += 1;        }         let mut tab_edits = Vec::with_capacity(fold_edits.len());         if old_snapshot.tab_size == new_snapshot.tab_size {            // Expand each edit to include the next tab on the same line as the edit,            // and any subsequent tabs on that line that moved across the tab expansion            // boundary.            for fold_edit in &mut fold_edits {                let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot);                let old_end_row_successor_offset = cmp::min(                    FoldPoint::new(old_end.row() + 1, 0),                    old_snapshot.fold_snapshot.max_point(),                )                .to_offset(&old_snapshot.fold_snapshot);                let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);                 let mut offset_from_edit = 0;                let mut first_tab_offset = None;                let mut last_tab_with_changed_expansion_offset = None;                'outer: for chunk in old_snapshot.fold_snapshot.chunks(                    fold_edit.old.end..old_end_row_successor_offset,                    false,                    Highlights::default(),                ) {                    for (ix, _) in chunk.text.match_indices('\t') {                        let offset_from_edit = offset_from_edit + (ix as u32);                        if first_tab_offset.is_none() {                            first_tab_offset = Some(offset_from_edit);                        }                         let old_column = old_end.column() + offset_from_edit;                        let new_column = new_end.column() + offset_from_edit;                        let was_expanded = old_column < old_snapshot.max_expansion_column;                        let is_expanded = new_column < new_snapshot.max_expansion_column;                        if was_expanded != is_expanded {                            last_tab_with_changed_expansion_offset = Some(offset_from_edit);                        } else if !was_expanded && !is_expanded {                            break 'outer;                        }                    }                     offset_from_edit += chunk.text.len() as u32;                    if old_end.column() + offset_from_edit >= old_snapshot.max_expansion_column                        && new_end.column() + offset_from_edit >= new_snapshot.max_expansion_column                    {                        break;                    }                }                 if let Some(offset) = last_tab_with_changed_expansion_offset.or(first_tab_offset) {                    fold_edit.old.end.0 += offset as usize + 1;                    fold_edit.new.end.0 += offset as usize + 1;                }            }             let _old_alloc_ptr = fold_edits.as_ptr();            // Combine any edits that overlap due to the expansion.            let mut fold_edits = fold_edits.into_iter();            let fold_edits = if let Some(mut first_edit) = fold_edits.next() {                // This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.                #[allow(clippy::filter_map_identity)]                let mut v: Vec<_> = fold_edits                    .scan(&mut first_edit, |state, edit| {                        if state.old.end >= edit.old.start {                            state.old.end = edit.old.end;                            state.new.end = edit.new.end;                            Some(None) // Skip this edit, it's merged                        } else {                            let new_state = edit.clone();                            let result = Some(Some(state.clone())); // Yield the previous edit                            **state = new_state;                            result                        }                    })                    .filter_map(|x| x)                    .collect();                v.push(first_edit);                debug_assert_eq!(v.as_ptr(), _old_alloc_ptr, "Fold edits were reallocated");                v            } else {                vec![]            };             for fold_edit in fold_edits {                let old_start = fold_edit.old.start.to_point(&old_snapshot.fold_snapshot);                let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot);                let new_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);                let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);                tab_edits.push(TabEdit {                    old: old_snapshot.to_tab_point(old_start)..old_snapshot.to_tab_point(old_end),                    new: new_snapshot.to_tab_point(new_start)..new_snapshot.to_tab_point(new_end),                });            }        } else {            new_snapshot.version += 1;            tab_edits.push(TabEdit {                old: TabPoint::zero()..old_snapshot.max_point(),                new: TabPoint::zero()..new_snapshot.max_point(),            });        }         *old_snapshot = new_snapshot;        (old_snapshot.clone(), tab_edits)    }} #[derive(Clone)]pub struct TabSnapshot {    pub fold_snapshot: FoldSnapshot,    pub tab_size: NonZeroU32,    pub max_expansion_column: u32,    pub version: usize,} impl TabSnapshot {    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {        &self.fold_snapshot.inlay_snapshot.buffer    }     pub fn line_len(&self, row: u32) -> u32 {        let max_point = self.max_point();        if row < max_point.row() {            self.to_tab_point(FoldPoint::new(row, self.fold_snapshot.line_len(row)))                .0                .column        } else {            max_point.column()        }    }     pub fn text_summary(&self) -> TextSummary {        self.text_summary_for_range(TabPoint::zero()..self.max_point())    }     pub fn text_summary_for_range(&self, range: Range<TabPoint>) -> TextSummary {        let input_start = self.to_fold_point(range.start, Bias::Left).0;        let input_end = self.to_fold_point(range.end, Bias::Right).0;        let input_summary = self            .fold_snapshot            .text_summary_for_range(input_start..input_end);         let mut first_line_chars = 0;        let line_end = if range.start.row() == range.end.row() {            range.end        } else {            self.max_point()        };        for c in self            .chunks(range.start..line_end, false, Highlights::default())            .flat_map(|chunk| chunk.text.chars())        {            if c == '\n' {                break;            }            first_line_chars += 1;        }         let mut last_line_chars = 0;        if range.start.row() == range.end.row() {            last_line_chars = first_line_chars;        } else {            for _ in self                .chunks(                    TabPoint::new(range.end.row(), 0)..range.end,                    false,                    Highlights::default(),                )                .flat_map(|chunk| chunk.text.chars())            {                last_line_chars += 1;            }        }         TextSummary {            lines: range.end.0 - range.start.0,            first_line_chars,            last_line_chars,            longest_row: input_summary.longest_row,            longest_row_chars: input_summary.longest_row_chars,        }    }     pub fn chunks<'a>(        &'a self,        range: Range<TabPoint>,        language_aware: bool,        highlights: Highlights<'a>,    ) -> TabChunks<'a> {        let (input_start, expanded_char_column, to_next_stop) =            self.to_fold_point(range.start, Bias::Left);        let input_column = input_start.column();        let input_start = input_start.to_offset(&self.fold_snapshot);        let input_end = self            .to_fold_point(range.end, Bias::Right)            .0            .to_offset(&self.fold_snapshot);        let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {            range.end.column() - range.start.column()        } else {            to_next_stop        };         TabChunks {            snapshot: self,            fold_chunks: self.fold_snapshot.chunks(                input_start..input_end,                language_aware,                highlights,            ),            input_column,            column: expanded_char_column,            max_expansion_column: self.max_expansion_column,            output_position: range.start.0,            max_output_position: range.end.0,            tab_size: self.tab_size,            chunk: Chunk {                text: &SPACES[0..(to_next_stop as usize)],                is_tab: true,                ..Default::default()            },            inside_leading_tab: to_next_stop > 0,        }    }     pub fn rows(&self, row: u32) -> fold_map::FoldRows<'_> {        self.fold_snapshot.row_infos(row)    }     #[cfg(test)]    pub fn text(&self) -> String {        self.chunks(            TabPoint::zero()..self.max_point(),            false,            Highlights::default(),        )        .map(|chunk| chunk.text)        .collect()    }     pub fn max_point(&self) -> TabPoint {        self.to_tab_point(self.fold_snapshot.max_point())    }     pub fn clip_point(&self, point: TabPoint, bias: Bias) -> TabPoint {        self.to_tab_point(            self.fold_snapshot                .clip_point(self.to_fold_point(point, bias).0, bias),        )    }     pub fn to_tab_point(&self, input: FoldPoint) -> TabPoint {        let chars = self.fold_snapshot.chars_at(FoldPoint::new(input.row(), 0));        let expanded = self.expand_tabs(chars, input.column());        TabPoint::new(input.row(), expanded)    }     pub fn to_fold_point(&self, output: TabPoint, bias: Bias) -> (FoldPoint, u32, u32) {        let chars = self.fold_snapshot.chars_at(FoldPoint::new(output.row(), 0));        let expanded = output.column();        let (collapsed, expanded_char_column, to_next_stop) =            self.collapse_tabs(chars, expanded, bias);        (            FoldPoint::new(output.row(), collapsed),            expanded_char_column,            to_next_stop,        )    }     pub fn make_tab_point(&self, point: Point, bias: Bias) -> TabPoint {        let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);        let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);        self.to_tab_point(fold_point)    }     pub fn to_point(&self, point: TabPoint, bias: Bias) -> Point {        let fold_point = self.to_fold_point(point, bias).0;        let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);        self.fold_snapshot            .inlay_snapshot            .to_buffer_point(inlay_point)    }     fn expand_tabs(&self, chars: impl Iterator<Item = char>, column: u32) -> u32 {        let tab_size = self.tab_size.get();         let mut expanded_chars = 0;        let mut expanded_bytes = 0;        let mut collapsed_bytes = 0;        let end_column = column.min(self.max_expansion_column);        for c in chars {            if collapsed_bytes >= end_column {                break;            }            if c == '\t' {                let tab_len = tab_size - expanded_chars % tab_size;                expanded_bytes += tab_len;                expanded_chars += tab_len;            } else {                expanded_bytes += c.len_utf8() as u32;                expanded_chars += 1;            }            collapsed_bytes += c.len_utf8() as u32;        }        expanded_bytes + column.saturating_sub(collapsed_bytes)    }     fn collapse_tabs(        &self,        chars: impl Iterator<Item = char>,        column: u32,        bias: Bias,    ) -> (u32, u32, u32) {        let tab_size = self.tab_size.get();         let mut expanded_bytes = 0;        let mut expanded_chars = 0;        let mut collapsed_bytes = 0;        for c in chars {            if expanded_bytes >= column {                break;            }            if collapsed_bytes >= self.max_expansion_column {                break;            }             if c == '\t' {                let tab_len = tab_size - (expanded_chars % tab_size);                expanded_chars += tab_len;                expanded_bytes += tab_len;                if expanded_bytes > column {                    expanded_chars -= expanded_bytes - column;                    return match bias {                        Bias::Left => (collapsed_bytes, expanded_chars, expanded_bytes - column),                        Bias::Right => (collapsed_bytes + 1, expanded_chars, 0),                    };                }            } else {                expanded_chars += 1;                expanded_bytes += c.len_utf8() as u32;            }             if expanded_bytes > column && matches!(bias, Bias::Left) {                expanded_chars -= 1;                break;            }             collapsed_bytes += c.len_utf8() as u32;        }        (            collapsed_bytes + column.saturating_sub(expanded_bytes),            expanded_chars,            0,        )    }} #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct TabPoint(pub Point); impl TabPoint {    pub fn new(row: u32, column: u32) -> Self {        Self(Point::new(row, column))    }     pub fn zero() -> Self {        Self::new(0, 0)    }     pub fn row(self) -> u32 {        self.0.row    }     pub fn column(self) -> u32 {        self.0.column    }} impl From<Point> for TabPoint {    fn from(point: Point) -> Self {        Self(point)    }} pub type TabEdit = text::Edit<TabPoint>; #[derive(Clone, Debug, Default, Eq, PartialEq)]pub struct TextSummary {    pub lines: Point,    pub first_line_chars: u32,    pub last_line_chars: u32,    pub longest_row: u32,    pub longest_row_chars: u32,} impl<'a> From<&'a str> for TextSummary {    fn from(text: &'a str) -> Self {        let sum = text::TextSummary::from(text);         TextSummary {            lines: sum.lines,            first_line_chars: sum.first_line_chars,            last_line_chars: sum.last_line_chars,            longest_row: sum.longest_row,            longest_row_chars: sum.longest_row_chars,        }    }} impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {    fn add_assign(&mut self, other: &'a Self) {        let joined_chars = self.last_line_chars + other.first_line_chars;        if joined_chars > self.longest_row_chars {            self.longest_row = self.lines.row;            self.longest_row_chars = joined_chars;        }        if other.longest_row_chars > self.longest_row_chars {            self.longest_row = self.lines.row + other.longest_row;            self.longest_row_chars = other.longest_row_chars;        }         if self.lines.row == 0 {            self.first_line_chars += other.first_line_chars;        }         if other.lines.row == 0 {            self.last_line_chars += other.first_line_chars;        } else {            self.last_line_chars = other.last_line_chars;        }         self.lines += &other.lines;    }} // Handles a tab width <= 16const SPACES: &str = "                "; pub struct TabChunks<'a> {    snapshot: &'a TabSnapshot,    fold_chunks: FoldChunks<'a>,    chunk: Chunk<'a>,    column: u32,    max_expansion_column: u32,    output_position: Point,    input_column: u32,    max_output_position: Point,    tab_size: NonZeroU32,    inside_leading_tab: bool,} 

Lines 1–500 of 814.