2040 lines · 74.2 KB · Rust
RawDownload
use super::{    wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},    Highlights,};use crate::{EditorStyle, GutterDimensions};use collections::{Bound, HashMap, HashSet};use gpui::{AnyElement, Pixels, WindowContext};use language::{BufferSnapshot, Chunk, Patch, Point};use multi_buffer::{Anchor, ExcerptId, ExcerptRange, MultiBufferRow, ToPoint as _};use parking_lot::Mutex;use std::{    cell::RefCell,    cmp::{self, Ordering},    fmt::Debug,    ops::{Deref, DerefMut, Range, RangeBounds},    sync::{        atomic::{AtomicUsize, Ordering::SeqCst},        Arc,    },};use sum_tree::{Bias, SumTree};use text::Edit; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; /// Tracks custom blocks such as diagnostics that should be displayed within buffer.////// See the [`display_map` module documentation](crate::display_map) for more information.pub struct BlockMap {    next_block_id: AtomicUsize,    wrap_snapshot: RefCell<WrapSnapshot>,    blocks: Vec<Arc<Block>>,    transforms: RefCell<SumTree<Transform>>,    show_excerpt_controls: bool,    buffer_header_height: u8,    excerpt_header_height: u8,    excerpt_footer_height: u8,} pub struct BlockMapReader<'a> {    blocks: &'a Vec<Arc<Block>>,    pub snapshot: BlockSnapshot,} pub struct BlockMapWriter<'a>(&'a mut BlockMap); #[derive(Clone)]pub struct BlockSnapshot {    wrap_snapshot: WrapSnapshot,    transforms: SumTree<Transform>,} #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]pub struct BlockId(usize); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct BlockPoint(pub Point); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct BlockRow(pub(super) u32); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]struct WrapRow(u32); pub type RenderBlock = Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>; pub struct Block {    id: BlockId,    position: Anchor,    height: u8,    style: BlockStyle,    render: Mutex<RenderBlock>,    disposition: BlockDisposition,} pub struct BlockProperties<P> {    pub position: P,    pub height: u8,    pub style: BlockStyle,    pub render: Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>,    pub disposition: BlockDisposition,} #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]pub enum BlockStyle {    Fixed,    Flex,    Sticky,} pub struct BlockContext<'a, 'b> {    pub context: &'b mut WindowContext<'a>,    pub anchor_x: Pixels,    pub max_width: Pixels,    pub gutter_dimensions: &'b GutterDimensions,    pub em_width: Pixels,    pub line_height: Pixels,    pub block_id: usize,    pub editor_style: &'b EditorStyle,} /// Whether the block should be considered above or below the anchor line#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]pub enum BlockDisposition {    Above,    Below,} #[derive(Clone, Debug)]struct Transform {    summary: TransformSummary,    block: Option<TransformBlock>,} pub(crate) enum BlockType {    Custom(BlockId),    Header,    Footer,} pub(crate) trait BlockLike {    fn block_type(&self) -> BlockType;    fn disposition(&self) -> BlockDisposition;} #[allow(clippy::large_enum_variant)]#[derive(Clone)]pub enum TransformBlock {    Custom(Arc<Block>),    ExcerptHeader {        id: ExcerptId,        buffer: BufferSnapshot,        range: ExcerptRange<text::Anchor>,        height: u8,        starts_new_buffer: bool,        show_excerpt_controls: bool,    },    ExcerptFooter {        id: ExcerptId,        disposition: BlockDisposition,        height: u8,    },} impl BlockLike for TransformBlock {    fn block_type(&self) -> BlockType {        match self {            TransformBlock::Custom(block) => BlockType::Custom(block.id),            TransformBlock::ExcerptHeader { .. } => BlockType::Header,            TransformBlock::ExcerptFooter { .. } => BlockType::Footer,        }    }     fn disposition(&self) -> BlockDisposition {        self.disposition()    }} impl TransformBlock {    fn disposition(&self) -> BlockDisposition {        match self {            TransformBlock::Custom(block) => block.disposition,            TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,            TransformBlock::ExcerptFooter { disposition, .. } => *disposition,        }    }     pub fn height(&self) -> u8 {        match self {            TransformBlock::Custom(block) => block.height,            TransformBlock::ExcerptHeader { height, .. } => *height,            TransformBlock::ExcerptFooter { height, .. } => *height,        }    }} impl Debug for TransformBlock {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        match self {            Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),            Self::ExcerptHeader {                buffer,                starts_new_buffer,                id,                ..            } => f                .debug_struct("ExcerptHeader")                .field("id", &id)                .field("path", &buffer.file().map(|f| f.path()))                .field("starts_new_buffer", &starts_new_buffer)                .finish(),            TransformBlock::ExcerptFooter {                id, disposition, ..            } => f                .debug_struct("ExcerptFooter")                .field("id", &id)                .field("disposition", &disposition)                .finish(),        }    }} #[derive(Clone, Debug, Default)]struct TransformSummary {    input_rows: u32,    output_rows: u32,} pub struct BlockChunks<'a> {    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,    input_chunks: wrap_map::WrapChunks<'a>,    input_chunk: Chunk<'a>,    output_row: u32,    max_output_row: u32,} #[derive(Clone)]pub struct BlockBufferRows<'a> {    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,    input_buffer_rows: wrap_map::WrapBufferRows<'a>,    output_row: BlockRow,    started: bool,} impl BlockMap {    pub fn new(        wrap_snapshot: WrapSnapshot,        show_excerpt_controls: bool,        buffer_header_height: u8,        excerpt_header_height: u8,        excerpt_footer_height: u8,    ) -> Self {        let row_count = wrap_snapshot.max_point().row() + 1;        let map = Self {            next_block_id: AtomicUsize::new(0),            blocks: Vec::new(),            transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),            show_excerpt_controls,            buffer_header_height,            excerpt_header_height,            excerpt_footer_height,        };        map.sync(            &wrap_snapshot,            Patch::new(vec![Edit {                old: 0..row_count,                new: 0..row_count,            }]),        );        map    }     pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapReader {        self.sync(&wrap_snapshot, edits);        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();        BlockMapReader {            blocks: &self.blocks,            snapshot: BlockSnapshot {                wrap_snapshot,                transforms: self.transforms.borrow().clone(),            },        }    }     pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter {        self.sync(&wrap_snapshot, edits);        *self.wrap_snapshot.borrow_mut() = wrap_snapshot;        BlockMapWriter(self)    }     fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {        let buffer = wrap_snapshot.buffer_snapshot();         // Handle changing the last excerpt if it is empty.        if buffer.trailing_excerpt_update_count()            != self                .wrap_snapshot                .borrow()                .buffer_snapshot()                .trailing_excerpt_update_count()        {            let max_point = wrap_snapshot.max_point();            let edit_start = wrap_snapshot.prev_row_boundary(max_point);            let edit_end = max_point.row() + 1;            edits = edits.compose([WrapEdit {                old: edit_start..edit_end,                new: edit_start..edit_end,            }]);        }         let edits = edits.into_inner();        if edits.is_empty() {            return;        }         let mut transforms = self.transforms.borrow_mut();        let mut new_transforms = SumTree::new();        let old_row_count = transforms.summary().input_rows;        let new_row_count = wrap_snapshot.max_point().row() + 1;        let mut cursor = transforms.cursor::<WrapRow>();        let mut last_block_ix = 0;        let mut blocks_in_edit = Vec::new();        let mut edits = edits.into_iter().peekable();         while let Some(edit) = edits.next() {            // Preserve any old transforms that precede this edit.            let old_start = WrapRow(edit.old.start);            let new_start = WrapRow(edit.new.start);            new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());            if let Some(transform) = cursor.item() {                if transform.is_isomorphic() && old_start == cursor.end(&()) {                    new_transforms.push(transform.clone(), &());                    cursor.next(&());                    while let Some(transform) = cursor.item() {                        if transform                            .block                            .as_ref()                            .map_or(false, |b| b.disposition().is_below())                        {                            new_transforms.push(transform.clone(), &());                            cursor.next(&());                        } else {                            break;                        }                    }                }            }             // Preserve any portion of an old transform that precedes this edit.            let extent_before_edit = old_start.0 - cursor.start().0;            push_isomorphic(&mut new_transforms, extent_before_edit);             // Skip over any old transforms that intersect this edit.            let mut old_end = WrapRow(edit.old.end);            let mut new_end = WrapRow(edit.new.end);            cursor.seek(&old_end, Bias::Left, &());            cursor.next(&());            if old_end == *cursor.start() {                while let Some(transform) = cursor.item() {                    if transform                        .block                        .as_ref()                        .map_or(false, |b| b.disposition().is_below())                    {                        cursor.next(&());                    } else {                        break;                    }                }            }             // Combine this edit with any subsequent edits that intersect the same transform.            while let Some(next_edit) = edits.peek() {                if next_edit.old.start <= cursor.start().0 {                    old_end = WrapRow(next_edit.old.end);                    new_end = WrapRow(next_edit.new.end);                    cursor.seek(&old_end, Bias::Left, &());                    cursor.next(&());                    if old_end == *cursor.start() {                        while let Some(transform) = cursor.item() {                            if transform                                .block                                .as_ref()                                .map_or(false, |b| b.disposition().is_below())                            {                                cursor.next(&());                            } else {                                break;                            }                        }                    }                    edits.next();                } else {                    break;                }            }             // Find the blocks within this edited region.            let new_buffer_start =                wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);            let start_bound = Bound::Included(new_buffer_start);            let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {                probe                    .position                    .to_point(buffer)                    .cmp(&new_buffer_start)                    .then(Ordering::Greater)            }) {                Ok(ix) | Err(ix) => last_block_ix + ix,            };             let end_bound;            let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {                end_bound = Bound::Unbounded;                self.blocks.len()            } else {                let new_buffer_end =                    wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);                end_bound = Bound::Excluded(new_buffer_end);                match self.blocks[start_block_ix..].binary_search_by(|probe| {                    probe                        .position                        .to_point(buffer)                        .cmp(&new_buffer_end)                        .then(Ordering::Greater)                }) {                    Ok(ix) | Err(ix) => start_block_ix + ix,                }            };            last_block_ix = end_block_ix;             debug_assert!(blocks_in_edit.is_empty());            blocks_in_edit.extend(                self.blocks[start_block_ix..end_block_ix]                    .iter()                    .map(|block| {                        let mut position = block.position.to_point(buffer);                        match block.disposition {                            BlockDisposition::Above => position.column = 0,                            BlockDisposition::Below => {                                position.column = buffer.line_len(MultiBufferRow(position.row))                            }                        }                        let position = wrap_snapshot.make_wrap_point(position, Bias::Left);                        (position.row(), TransformBlock::Custom(block.clone()))                    }),            );             if buffer.show_headers() {                blocks_in_edit.extend(BlockMap::header_blocks(                    self.show_excerpt_controls,                    self.excerpt_footer_height,                    self.buffer_header_height,                    self.excerpt_header_height,                    buffer,                    (start_bound, end_bound),                    wrap_snapshot,                ));            }             BlockMap::sort_blocks(&mut blocks_in_edit);             // For each of these blocks, insert a new isomorphic transform preceding the block,            // and then insert the block itself.            for (block_row, block) in blocks_in_edit.drain(..) {                let insertion_row = match block.disposition() {                    BlockDisposition::Above => block_row,                    BlockDisposition::Below => block_row + 1,                };                let extent_before_block = insertion_row - new_transforms.summary().input_rows;                push_isomorphic(&mut new_transforms, extent_before_block);                new_transforms.push(Transform::block(block), &());            }             old_end = WrapRow(old_end.0.min(old_row_count));            new_end = WrapRow(new_end.0.min(new_row_count));             // Insert an isomorphic transform after the final block.            let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;            push_isomorphic(&mut new_transforms, extent_after_last_block);             // Preserve any portion of the old transform after this edit.            let extent_after_edit = cursor.start().0 - old_end.0;            push_isomorphic(&mut new_transforms, extent_after_edit);        }         new_transforms.append(cursor.suffix(&()), &());        debug_assert_eq!(            new_transforms.summary().input_rows,            wrap_snapshot.max_point().row() + 1        );         drop(cursor);        *transforms = new_transforms;    }     pub fn replace_renderers(&mut self, mut renderers: HashMap<BlockId, RenderBlock>) {        for block in &mut self.blocks {            if let Some(render) = renderers.remove(&block.id) {                *block.render.lock() = render;            }        }    }     pub fn show_excerpt_controls(&self) -> bool {        self.show_excerpt_controls    }     pub fn header_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(        show_excerpt_controls: bool,        excerpt_footer_height: u8,        buffer_header_height: u8,        excerpt_header_height: u8,        buffer: &'b multi_buffer::MultiBufferSnapshot,        range: R,        wrap_snapshot: &'c WrapSnapshot,    ) -> impl Iterator<Item = (u32, TransformBlock)> + 'b    where        R: RangeBounds<T>,

Lines 1–500 of 2040.