use super::{ Highlights, fold_map::Chunk, wrap_map::{self, WrapEdit, WrapPatch, WrapPoint, WrapSnapshot},};use crate::{ EditorStyle, GutterDimensions, display_map::{Companion, dimensions::RowDelta, wrap_map::WrapRow},};use collections::{Bound, HashMap, HashSet};use gpui::{AnyElement, App, EntityId, Pixels, Window};use language::{LanguageAwareStyling, Patch, Point};use multi_buffer::{ Anchor, ExcerptBoundaryInfo, MultiBuffer, MultiBufferOffset, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint as _,};use parking_lot::Mutex;use std::{ cell::{Cell, RefCell}, cmp::{self, Ordering}, fmt::Debug, ops::{Deref, DerefMut, Not, Range, RangeBounds, RangeInclusive}, sync::{ Arc, atomic::{AtomicUsize, Ordering::SeqCst}, },};use sum_tree::{Bias, ContextLessSummary, Dimensions, SumTree, TreeMap};use text::{BufferId, Edit};use ui::{ElementId, IntoElement}; const NEWLINES: &[u8; rope::Chunk::MASK_BITS] = &[b'\n'; _];const BULLETS: &[u8; rope::Chunk::MASK_BITS] = &[b'*'; _]; /// 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 { pub(super) wrap_snapshot: RefCell<WrapSnapshot>, next_block_id: AtomicUsize, custom_blocks: Vec<Arc<CustomBlock>>, custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>, transforms: RefCell<SumTree<Transform>>, buffer_header_height: u32, excerpt_header_height: u32, pub(super) folded_buffers: HashSet<BufferId>, buffers_with_disabled_headers: HashSet<BufferId>, pub(super) deferred_edits: Cell<Patch<WrapRow>>,} pub struct BlockMapReader<'a> { pub blocks: &'a Vec<Arc<CustomBlock>>, pub snapshot: BlockSnapshot,} pub struct BlockMapWriter<'a> { block_map: &'a mut BlockMap, companion: Option<BlockMapWriterCompanion<'a>>,} /// Auxiliary data needed when modifying a BlockMap whose parent DisplayMap has a companion.struct BlockMapWriterCompanion<'a> { display_map_id: EntityId, companion_wrap_snapshot: WrapSnapshot, companion: &'a Companion, inverse: Option<BlockMapInverseWriter<'a>>,} struct BlockMapInverseWriter<'a> { companion_multibuffer: &'a MultiBuffer, companion_writer: Box<BlockMapWriter<'a>>,} #[derive(Clone)]pub struct BlockSnapshot { pub(super) wrap_snapshot: WrapSnapshot, transforms: SumTree<Transform>, custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>, pub(super) buffer_header_height: u32, pub(super) excerpt_header_height: u32, pub(super) buffers_with_disabled_headers: HashSet<BufferId>,} impl Deref for BlockSnapshot { type Target = WrapSnapshot; fn deref(&self) -> &Self::Target { &self.wrap_snapshot }} #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]pub struct CustomBlockId(pub usize); impl From<CustomBlockId> for ElementId { fn from(val: CustomBlockId) -> Self { val.0.into() }} #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]pub struct SpacerId(pub usize); /// A zero-indexed point in a text buffer consisting of a row and column/// adjusted for inserted blocks, wrapped rows, tabs, folds and inlays.#[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 u32); impl_for_row_types! { BlockRow => RowDelta} impl BlockPoint { pub fn row(&self) -> BlockRow { BlockRow(self.0.row) }} pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>; /// Where to place a block.#[derive(Clone, Debug, Eq, PartialEq)]pub enum BlockPlacement<T> { /// Place the block above the given position. Above(T), /// Place the block below the given position. Below(T), /// Place the block next the given position. Near(T), /// Replace the given range of positions with the block. Replace(RangeInclusive<T>),} impl<T> BlockPlacement<T> { pub fn start(&self) -> &T { match self { BlockPlacement::Above(position) => position, BlockPlacement::Below(position) => position, BlockPlacement::Near(position) => position, BlockPlacement::Replace(range) => range.start(), } } fn end(&self) -> &T { match self { BlockPlacement::Above(position) => position, BlockPlacement::Below(position) => position, BlockPlacement::Near(position) => position, BlockPlacement::Replace(range) => range.end(), } } pub fn as_ref(&self) -> BlockPlacement<&T> { match self { BlockPlacement::Above(position) => BlockPlacement::Above(position), BlockPlacement::Below(position) => BlockPlacement::Below(position), BlockPlacement::Near(position) => BlockPlacement::Near(position), BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()), } } pub fn map<R>(self, mut f: impl FnMut(T) -> R) -> BlockPlacement<R> { match self { BlockPlacement::Above(position) => BlockPlacement::Above(f(position)), BlockPlacement::Below(position) => BlockPlacement::Below(f(position)), BlockPlacement::Near(position) => BlockPlacement::Near(f(position)), BlockPlacement::Replace(range) => { let (start, end) = range.into_inner(); BlockPlacement::Replace(f(start)..=f(end)) } } } fn tie_break(&self) -> u8 { match self { BlockPlacement::Replace(_) => 0, BlockPlacement::Above(_) => 1, BlockPlacement::Near(_) => 2, BlockPlacement::Below(_) => 3, } }} impl BlockPlacement<Anchor> { #[ztracing::instrument(skip_all)] fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering { self.start() .cmp(other.start(), buffer) .then_with(|| other.end().cmp(self.end(), buffer)) .then_with(|| self.tie_break().cmp(&other.tie_break())) } #[ztracing::instrument(skip_all)] fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> { let buffer_snapshot = wrap_snapshot.buffer_snapshot(); match self { BlockPlacement::Above(position) => { let mut position = position.to_point(buffer_snapshot); position.column = 0; let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row(); Some(BlockPlacement::Above(wrap_row)) } BlockPlacement::Near(position) => { let mut position = position.to_point(buffer_snapshot); position.column = buffer_snapshot.line_len(MultiBufferRow(position.row)); let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row(); Some(BlockPlacement::Near(wrap_row)) } BlockPlacement::Below(position) => { let mut position = position.to_point(buffer_snapshot); position.column = buffer_snapshot.line_len(MultiBufferRow(position.row)); let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row(); Some(BlockPlacement::Below(wrap_row)) } BlockPlacement::Replace(range) => { let mut start = range.start().to_point(buffer_snapshot); let mut end = range.end().to_point(buffer_snapshot); if start == end { None } else { start.column = 0; let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row(); end.column = buffer_snapshot.line_len(MultiBufferRow(end.row)); let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row(); Some(BlockPlacement::Replace(start_wrap_row..=end_wrap_row)) } } } }} pub struct CustomBlock { pub id: CustomBlockId, pub placement: BlockPlacement<Anchor>, pub height: Option<u32>, style: BlockStyle, render: Arc<Mutex<RenderBlock>>, priority: usize,} #[derive(Clone)]pub struct BlockProperties<P> { pub placement: BlockPlacement<P>, // None if the block takes up no space // (e.g. a horizontal line) pub height: Option<u32>, pub style: BlockStyle, pub render: RenderBlock, pub priority: usize,} impl<P: Debug> Debug for BlockProperties<P> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BlockProperties") .field("placement", &self.placement) .field("height", &self.height) .field("style", &self.style) .finish() }} #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]pub enum BlockStyle { Fixed, Flex, /// Like `Flex` but doesn't use the gutter: /// - block content scrolls with buffer content /// - doesn't paint in gutter Spacer, Sticky,} #[derive(Debug, Default, Copy, Clone)]pub struct EditorMargins { pub gutter: GutterDimensions, pub right: Pixels, pub extended_right: Pixels,} #[derive(gpui::AppContext, gpui::VisualContext)]pub struct BlockContext<'a, 'b> { #[window] pub window: &'a mut Window, #[app] pub app: &'b mut App, pub anchor_x: Pixels, pub max_width: Pixels, pub margins: &'b EditorMargins, pub em_width: Pixels, pub line_height: Pixels, pub block_id: BlockId, pub height: u32, pub selected: bool, pub editor_style: &'b EditorStyle, pub indent_guide_padding: Pixels,} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum BlockId { ExcerptBoundary(Anchor), FoldedBuffer(BufferId), Custom(CustomBlockId), Spacer(SpacerId),} impl From<BlockId> for ElementId { fn from(value: BlockId) -> Self { match value { BlockId::Custom(CustomBlockId(id)) => ("Block", id).into(), BlockId::ExcerptBoundary(anchor) => anchor.opaque_id().unwrap().into(), BlockId::FoldedBuffer(id) => ("FoldedBuffer", EntityId::from(id.to_proto())).into(), BlockId::Spacer(SpacerId(id)) => ("Spacer", id).into(), } }} impl std::fmt::Display for BlockId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Custom(id) => write!(f, "Block({id:?})"), Self::ExcerptBoundary(id) => write!(f, "ExcerptBoundary({id:?})"), Self::FoldedBuffer(id) => write!(f, "FoldedBuffer({id:?})"), Self::Spacer(id) => write!(f, "Spacer({id:?})"), } }} #[derive(Clone, Debug)]struct Transform { summary: TransformSummary, block: Option<Block>,} #[derive(Clone)]pub enum Block { Custom(Arc<CustomBlock>), FoldedBuffer { first_excerpt: ExcerptBoundaryInfo, height: u32, }, ExcerptBoundary { excerpt: ExcerptBoundaryInfo, height: u32, }, BufferHeader { excerpt: ExcerptBoundaryInfo, height: u32, }, Spacer { id: SpacerId, height: u32, is_below: bool, },} impl Block { pub fn id(&self) -> BlockId { match self { Block::Custom(block) => BlockId::Custom(block.id), Block::ExcerptBoundary { excerpt: next_excerpt, .. } => BlockId::ExcerptBoundary(next_excerpt.start_anchor), Block::FoldedBuffer { first_excerpt, .. } => { BlockId::FoldedBuffer(first_excerpt.buffer_id()) } Block::BufferHeader { excerpt: next_excerpt, .. } => BlockId::ExcerptBoundary(next_excerpt.start_anchor), Block::Spacer { id, .. } => BlockId::Spacer(*id), } } pub fn has_height(&self) -> bool { match self { Block::Custom(block) => block.height.is_some(), Block::ExcerptBoundary { .. } | Block::FoldedBuffer { .. } | Block::BufferHeader { .. } | Block::Spacer { .. } => true, } } pub fn height(&self) -> u32 { match self { Block::Custom(block) => block.height.unwrap_or(0), Block::ExcerptBoundary { height, .. } | Block::FoldedBuffer { height, .. } | Block::BufferHeader { height, .. } | Block::Spacer { height, .. } => *height, } } pub fn style(&self) -> BlockStyle { match self { Block::Custom(block) => block.style, Block::ExcerptBoundary { .. } | Block::FoldedBuffer { .. } | Block::BufferHeader { .. } => BlockStyle::Sticky, Block::Spacer { .. } => BlockStyle::Spacer, } } fn place_above(&self) -> bool { match self { Block::Custom(block) => matches!(block.placement, BlockPlacement::Above(_)), Block::FoldedBuffer { .. } => false, Block::ExcerptBoundary { .. } => true, Block::BufferHeader { .. } => true, Block::Spacer { is_below, .. } => !*is_below, } } pub fn place_near(&self) -> bool { match self { Block::Custom(block) => matches!(block.placement, BlockPlacement::Near(_)), Block::FoldedBuffer { .. } => false, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => false, Block::Spacer { .. } => false, } } fn place_below(&self) -> bool { match self { Block::Custom(block) => matches!( block.placement, BlockPlacement::Below(_) | BlockPlacement::Near(_) ), Block::FoldedBuffer { .. } => false, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => false, Block::Spacer { is_below, .. } => *is_below, } } fn is_replacement(&self) -> bool { match self { Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)), Block::FoldedBuffer { .. } => true, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => false, Block::Spacer { .. } => false, } } fn is_header(&self) -> bool { match self { Block::Custom(_) => false, Block::FoldedBuffer { .. } => true, Block::ExcerptBoundary { .. } => true, Block::BufferHeader { .. } => true, Block::Spacer { .. } => false, } } pub fn is_buffer_header(&self) -> bool { match self { Block::Custom(_) => false, Block::FoldedBuffer { .. } => true, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => true, Block::Spacer { .. } => false, } }} impl Debug for Block { 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::FoldedBuffer { first_excerpt, height, } => f .debug_struct("FoldedBuffer") .field("first_excerpt", &first_excerpt) .field("height", height) .finish(), Self::ExcerptBoundary { excerpt, height } => f .debug_struct("ExcerptBoundary") .field("excerpt", excerpt) .field("height", height) .finish(), Self::BufferHeader { excerpt, height } => f .debug_struct("BufferHeader") .field("excerpt", excerpt) .field("height", height) .finish(), Self::Spacer { id, height, is_below: _, } => f .debug_struct("Spacer") .field("id", id)Lines 1–500 of 4870. Load more
use super::{ Highlights, fold_map::Chunk, wrap_map::{self, WrapEdit, WrapPatch, WrapPoint, WrapSnapshot}, }; use crate::{ EditorStyle, GutterDimensions, display_map::{Companion, dimensions::RowDelta, wrap_map::WrapRow}, }; use collections::{Bound, HashMap, HashSet}; use gpui::{AnyElement, App, EntityId, Pixels, Window}; use language::{LanguageAwareStyling, Patch, Point}; use multi_buffer::{ Anchor, ExcerptBoundaryInfo, MultiBuffer, MultiBufferOffset, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint as _, }; use parking_lot::Mutex; use std::{ cell::{Cell, RefCell}, cmp::{self, Ordering}, fmt::Debug, ops::{Deref, DerefMut, Not, Range, RangeBounds, RangeInclusive}, sync::{ Arc, atomic::{AtomicUsize, Ordering::SeqCst}, }, }; use sum_tree::{Bias, ContextLessSummary, Dimensions, SumTree, TreeMap}; use text::{BufferId, Edit}; use ui::{ElementId, IntoElement}; const NEWLINES: &[u8; rope::Chunk::MASK_BITS] = &[b'\n'; _]; const BULLETS: &[u8; rope::Chunk::MASK_BITS] = &[b'*'; _]; /// 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 { pub(super) wrap_snapshot: RefCell<WrapSnapshot>, next_block_id: AtomicUsize, custom_blocks: Vec<Arc<CustomBlock>>, custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>, transforms: RefCell<SumTree<Transform>>, buffer_header_height: u32, excerpt_header_height: u32, pub(super) folded_buffers: HashSet<BufferId>, buffers_with_disabled_headers: HashSet<BufferId>, pub(super) deferred_edits: Cell<Patch<WrapRow>>, } pub struct BlockMapReader<'a> { pub blocks: &'a Vec<Arc<CustomBlock>>, pub snapshot: BlockSnapshot, } pub struct BlockMapWriter<'a> { block_map: &'a mut BlockMap, companion: Option<BlockMapWriterCompanion<'a>>, } /// Auxiliary data needed when modifying a BlockMap whose parent DisplayMap has a companion. struct BlockMapWriterCompanion<'a> { display_map_id: EntityId, companion_wrap_snapshot: WrapSnapshot, companion: &'a Companion, inverse: Option<BlockMapInverseWriter<'a>>, } struct BlockMapInverseWriter<'a> { companion_multibuffer: &'a MultiBuffer, companion_writer: Box<BlockMapWriter<'a>>, } #[derive(Clone)] pub struct BlockSnapshot { pub(super) wrap_snapshot: WrapSnapshot, transforms: SumTree<Transform>, custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>, pub(super) buffer_header_height: u32, pub(super) excerpt_header_height: u32, pub(super) buffers_with_disabled_headers: HashSet<BufferId>, } impl Deref for BlockSnapshot { type Target = WrapSnapshot; fn deref(&self) -> &Self::Target { &self.wrap_snapshot } } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CustomBlockId(pub usize); impl From<CustomBlockId> for ElementId { fn from(val: CustomBlockId) -> Self { val.0.into() } } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SpacerId(pub usize); /// A zero-indexed point in a text buffer consisting of a row and column /// adjusted for inserted blocks, wrapped rows, tabs, folds and inlays. #[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 u32); impl_for_row_types! { BlockRow => RowDelta } impl BlockPoint { pub fn row(&self) -> BlockRow { BlockRow(self.0.row) } } pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>; /// Where to place a block. #[derive(Clone, Debug, Eq, PartialEq)] pub enum BlockPlacement<T> { /// Place the block above the given position. Above(T), /// Place the block below the given position. Below(T), /// Place the block next the given position. Near(T), /// Replace the given range of positions with the block. Replace(RangeInclusive<T>), } impl<T> BlockPlacement<T> { pub fn start(&self) -> &T { match self { BlockPlacement::Above(position) => position, BlockPlacement::Below(position) => position, BlockPlacement::Near(position) => position, BlockPlacement::Replace(range) => range.start(), } } fn end(&self) -> &T { match self { BlockPlacement::Above(position) => position, BlockPlacement::Below(position) => position, BlockPlacement::Near(position) => position, BlockPlacement::Replace(range) => range.end(), } } pub fn as_ref(&self) -> BlockPlacement<&T> { match self { BlockPlacement::Above(position) => BlockPlacement::Above(position), BlockPlacement::Below(position) => BlockPlacement::Below(position), BlockPlacement::Near(position) => BlockPlacement::Near(position), BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()), } } pub fn map<R>(self, mut f: impl FnMut(T) -> R) -> BlockPlacement<R> { match self { BlockPlacement::Above(position) => BlockPlacement::Above(f(position)), BlockPlacement::Below(position) => BlockPlacement::Below(f(position)), BlockPlacement::Near(position) => BlockPlacement::Near(f(position)), BlockPlacement::Replace(range) => { let (start, end) = range.into_inner(); BlockPlacement::Replace(f(start)..=f(end)) } } } fn tie_break(&self) -> u8 { match self { BlockPlacement::Replace(_) => 0, BlockPlacement::Above(_) => 1, BlockPlacement::Near(_) => 2, BlockPlacement::Below(_) => 3, } } } impl BlockPlacement<Anchor> { #[ztracing::instrument(skip_all)] fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering { self.start() .cmp(other.start(), buffer) .then_with(|| other.end().cmp(self.end(), buffer)) .then_with(|| self.tie_break().cmp(&other.tie_break())) } #[ztracing::instrument(skip_all)] fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> { let buffer_snapshot = wrap_snapshot.buffer_snapshot(); match self { BlockPlacement::Above(position) => { let mut position = position.to_point(buffer_snapshot); position.column = 0; let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row(); Some(BlockPlacement::Above(wrap_row)) } BlockPlacement::Near(position) => { let mut position = position.to_point(buffer_snapshot); position.column = buffer_snapshot.line_len(MultiBufferRow(position.row)); let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row(); Some(BlockPlacement::Near(wrap_row)) } BlockPlacement::Below(position) => { let mut position = position.to_point(buffer_snapshot); position.column = buffer_snapshot.line_len(MultiBufferRow(position.row)); let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row(); Some(BlockPlacement::Below(wrap_row)) } BlockPlacement::Replace(range) => { let mut start = range.start().to_point(buffer_snapshot); let mut end = range.end().to_point(buffer_snapshot); if start == end { None } else { start.column = 0; let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row(); end.column = buffer_snapshot.line_len(MultiBufferRow(end.row)); let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row(); Some(BlockPlacement::Replace(start_wrap_row..=end_wrap_row)) } } } } } pub struct CustomBlock { pub id: CustomBlockId, pub placement: BlockPlacement<Anchor>, pub height: Option<u32>, style: BlockStyle, render: Arc<Mutex<RenderBlock>>, priority: usize, } #[derive(Clone)] pub struct BlockProperties<P> { pub placement: BlockPlacement<P>, // None if the block takes up no space // (e.g. a horizontal line) pub height: Option<u32>, pub style: BlockStyle, pub render: RenderBlock, pub priority: usize, } impl<P: Debug> Debug for BlockProperties<P> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BlockProperties") .field("placement", &self.placement) .field("height", &self.height) .field("style", &self.style) .finish() } } #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum BlockStyle { Fixed, Flex, /// Like `Flex` but doesn't use the gutter: /// - block content scrolls with buffer content /// - doesn't paint in gutter Spacer, Sticky, } #[derive(Debug, Default, Copy, Clone)] pub struct EditorMargins { pub gutter: GutterDimensions, pub right: Pixels, pub extended_right: Pixels, } #[derive(gpui::AppContext, gpui::VisualContext)] pub struct BlockContext<'a, 'b> { #[window] pub window: &'a mut Window, #[app] pub app: &'b mut App, pub anchor_x: Pixels, pub max_width: Pixels, pub margins: &'b EditorMargins, pub em_width: Pixels, pub line_height: Pixels, pub block_id: BlockId, pub height: u32, pub selected: bool, pub editor_style: &'b EditorStyle, pub indent_guide_padding: Pixels, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BlockId { ExcerptBoundary(Anchor), FoldedBuffer(BufferId), Custom(CustomBlockId), Spacer(SpacerId), } impl From<BlockId> for ElementId { fn from(value: BlockId) -> Self { match value { BlockId::Custom(CustomBlockId(id)) => ("Block", id).into(), BlockId::ExcerptBoundary(anchor) => anchor.opaque_id().unwrap().into(), BlockId::FoldedBuffer(id) => ("FoldedBuffer", EntityId::from(id.to_proto())).into(), BlockId::Spacer(SpacerId(id)) => ("Spacer", id).into(), } } } impl std::fmt::Display for BlockId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Custom(id) => write!(f, "Block({id:?})"), Self::ExcerptBoundary(id) => write!(f, "ExcerptBoundary({id:?})"), Self::FoldedBuffer(id) => write!(f, "FoldedBuffer({id:?})"), Self::Spacer(id) => write!(f, "Spacer({id:?})"), } } } #[derive(Clone, Debug)] struct Transform { summary: TransformSummary, block: Option<Block>, } #[derive(Clone)] pub enum Block { Custom(Arc<CustomBlock>), FoldedBuffer { first_excerpt: ExcerptBoundaryInfo, height: u32, }, ExcerptBoundary { excerpt: ExcerptBoundaryInfo, height: u32, }, BufferHeader { excerpt: ExcerptBoundaryInfo, height: u32, }, Spacer { id: SpacerId, height: u32, is_below: bool, }, } impl Block { pub fn id(&self) -> BlockId { match self { Block::Custom(block) => BlockId::Custom(block.id), Block::ExcerptBoundary { excerpt: next_excerpt, .. } => BlockId::ExcerptBoundary(next_excerpt.start_anchor), Block::FoldedBuffer { first_excerpt, .. } => { BlockId::FoldedBuffer(first_excerpt.buffer_id()) } Block::BufferHeader { excerpt: next_excerpt, .. } => BlockId::ExcerptBoundary(next_excerpt.start_anchor), Block::Spacer { id, .. } => BlockId::Spacer(*id), } } pub fn has_height(&self) -> bool { match self { Block::Custom(block) => block.height.is_some(), Block::ExcerptBoundary { .. } | Block::FoldedBuffer { .. } | Block::BufferHeader { .. } | Block::Spacer { .. } => true, } } pub fn height(&self) -> u32 { match self { Block::Custom(block) => block.height.unwrap_or(0), Block::ExcerptBoundary { height, .. } | Block::FoldedBuffer { height, .. } | Block::BufferHeader { height, .. } | Block::Spacer { height, .. } => *height, } } pub fn style(&self) -> BlockStyle { match self { Block::Custom(block) => block.style, Block::ExcerptBoundary { .. } | Block::FoldedBuffer { .. } | Block::BufferHeader { .. } => BlockStyle::Sticky, Block::Spacer { .. } => BlockStyle::Spacer, } } fn place_above(&self) -> bool { match self { Block::Custom(block) => matches!(block.placement, BlockPlacement::Above(_)), Block::FoldedBuffer { .. } => false, Block::ExcerptBoundary { .. } => true, Block::BufferHeader { .. } => true, Block::Spacer { is_below, .. } => !*is_below, } } pub fn place_near(&self) -> bool { match self { Block::Custom(block) => matches!(block.placement, BlockPlacement::Near(_)), Block::FoldedBuffer { .. } => false, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => false, Block::Spacer { .. } => false, } } fn place_below(&self) -> bool { match self { Block::Custom(block) => matches!( block.placement, BlockPlacement::Below(_) | BlockPlacement::Near(_) ), Block::FoldedBuffer { .. } => false, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => false, Block::Spacer { is_below, .. } => *is_below, } } fn is_replacement(&self) -> bool { match self { Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)), Block::FoldedBuffer { .. } => true, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => false, Block::Spacer { .. } => false, } } fn is_header(&self) -> bool { match self { Block::Custom(_) => false, Block::FoldedBuffer { .. } => true, Block::ExcerptBoundary { .. } => true, Block::BufferHeader { .. } => true, Block::Spacer { .. } => false, } } pub fn is_buffer_header(&self) -> bool { match self { Block::Custom(_) => false, Block::FoldedBuffer { .. } => true, Block::ExcerptBoundary { .. } => false, Block::BufferHeader { .. } => true, Block::Spacer { .. } => false, } } } impl Debug for Block { 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::FoldedBuffer { first_excerpt, height, } => f .debug_struct("FoldedBuffer") .field("first_excerpt", &first_excerpt) .field("height", height) .finish(), Self::ExcerptBoundary { excerpt, height } => f .debug_struct("ExcerptBoundary") .field("excerpt", excerpt) .field("height", height) .finish(), Self::BufferHeader { excerpt, height } => f .debug_struct("BufferHeader") .field("excerpt", excerpt) .field("height", height) .finish(), Self::Spacer { id, height, is_below: _, } => f .debug_struct("Spacer") .field("id", id)