diff --git a/crates/clock/src/clock.rs b/crates/clock/src/clock.rs index 3cbf8d6594..a781cf2d44 100644 --- a/crates/clock/src/clock.rs +++ b/crates/clock/src/clock.rs @@ -4,16 +4,21 @@ use std::{ fmt, iter, }; +/// A unique identifier for each distributed node pub type ReplicaId = u16; + +/// A [Lamport sequence number](https://en.wikipedia.org/wiki/Lamport_timestamp), pub type Seq = u32; +/// A [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp), +/// used to determine the ordering of events in the editor. #[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] pub struct Lamport { pub replica_id: ReplicaId, pub value: Seq, } -/// A vector clock +/// A [vector clock](https://en.wikipedia.org/wiki/Vector_clock) #[derive(Clone, Default, Hash, Eq, PartialEq)] pub struct Global(SmallVec<[u32; 8]>); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index f975a833c1..2f4c4ddc44 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -67,15 +67,27 @@ pub trait ToDisplayPoint { type TextHighlights = TreeMap, Arc<(HighlightStyle, Vec>)>>; type InlayHighlights = BTreeMap>; +/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints, +/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting. +/// +/// See the [module level documentation](self) for more information. pub struct DisplayMap { + /// The buffer that we are displaying. buffer: Model, buffer_subscription: BufferSubscription, - fold_map: FoldMap, + /// Decides where the [`Inlay`]s should be displayed. inlay_map: InlayMap, + /// Decides where the fold indicators should be and tracks parts of a source file that are currently folded. + fold_map: FoldMap, + /// Keeps track of hard tabs in a buffer. tab_map: TabMap, + /// Handles soft wrapping. wrap_map: Model, + /// Tracks custom blocks such as diagnostics that should be displayed within buffer. block_map: BlockMap, + /// Regions of text that should be highlighted. text_highlights: TextHighlights, + /// Regions of inlays that should be highlighted. inlay_highlights: InlayHighlights, pub clip_at_line_ends: bool, } diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 4ed50e9b61..a17dc4a6c6 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -22,6 +22,9 @@ 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, diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 61b973cc6c..07d40fcc12 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -178,6 +178,9 @@ impl<'a> FoldMapWriter<'a> { } } +/// Decides where the fold indicators should be; also tracks parts of a source file that are currently folded. +/// +/// See the [`display_map` module documentation](crate::display_map) for more information. pub(crate) struct FoldMap { snapshot: FoldSnapshot, ellipses_color: Option, diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index f1f52b4927..c26684a07b 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -16,6 +16,9 @@ use text::{Patch, Rope}; use super::Highlights; +/// Decides where the [`Inlay`]s should be displayed. +/// +/// See the [`display_map` module documentation](crate::display_map) for more information. pub struct InlayMap { snapshot: InlaySnapshot, inlays: Vec, diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index 6b38ea2d24..7efc6b193b 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -9,6 +9,9 @@ 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 { diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 39f9a2315b..bdf5815f13 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -15,6 +15,9 @@ use text::Patch; pub use super::tab_map::TextSummary; pub type WrapEdit = text::Edit; +/// 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)>, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c28ae6948d..039d47460d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -290,7 +290,7 @@ pub enum SelectPhase { } #[derive(Clone, Debug)] -pub(crate) enum SelectMode { +pub enum SelectMode { Character, Word(Range), Line(Range), @@ -349,10 +349,16 @@ type CompletionId = usize; type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec>); type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec); +/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`] +/// +/// See the [module level documentation](self) for more information. pub struct Editor { handle: WeakView, focus_handle: FocusHandle, + /// The text buffer being edited buffer: Model, + /// Map of how text in the buffer should be displayed. + /// Handles soft wraps, folds, fake inlay text insertions, etc. display_map: Model, pub selections: SelectionsCollection, pub scroll_manager: ScrollManager, diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 96f9507dd2..b0595ee778 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -29,8 +29,11 @@ pub struct SelectionsCollection { buffer: Model, pub next_selection_id: usize, pub line_mode: bool, - disjoint: Arc<[Selection]>, - pending: Option, + /// The non-pending, non-overlapping selections. + /// The [SelectionsCollection::pending] selection could possibly overlap these + pub disjoint: Arc<[Selection]>, + /// A pending selection, such as when the mouse is being dragged + pub pending: Option, } impl SelectionsCollection { diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 5dda11a91c..f6aaded23e 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -47,10 +47,17 @@ const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct ExcerptId(usize); +/// One or more [`Buffers`](Buffer) being edited in a single view. +/// +/// See pub struct MultiBuffer { + /// A snapshot of the [`Excerpt`]s in the MultiBuffer. + /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot. snapshot: RefCell, + /// Contains the state of the buffers being edited buffers: RefCell>, subscriptions: Topic, + /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`] singleton: bool, replica_id: ReplicaId, history: History, @@ -135,6 +142,7 @@ struct BufferState { _subscriptions: [gpui::Subscription; 2], } +/// The contents of a [`MultiBuffer`] at a single point in time. #[derive(Clone, Default)] pub struct MultiBufferSnapshot { singleton: bool, @@ -149,22 +157,36 @@ pub struct MultiBufferSnapshot { has_conflict: bool, } +/// A boundary between [`Excerpt`]s in a [`MultiBuffer`] pub struct ExcerptBoundary { pub id: ExcerptId, + /// The row in the `MultiBuffer` where the boundary is located pub row: u32, pub buffer: BufferSnapshot, pub range: ExcerptRange, + /// It's possible to have multiple excerpts in the same buffer, + /// and they are rendered together without a new File header. + /// + /// This flag indicates that the excerpt is the first one in the buffer. pub starts_new_buffer: bool, } +/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`]. #[derive(Clone)] struct Excerpt { + /// The unique identifier for this excerpt id: ExcerptId, + /// The location of the excerpt in the [`MultiBuffer`] locator: Locator, + /// The buffer being excerpted buffer_id: BufferId, + /// A snapshot of the buffer being excerpted buffer: BufferSnapshot, + /// The range of the buffer to be shown in the excerpt range: ExcerptRange, + /// The last row in the excerpted slice of the buffer max_buffer_row: u32, + /// A summary of the text in the excerpt text_summary: TextSummary, has_trailing_newline: bool, } @@ -175,16 +197,23 @@ struct ExcerptIdMapping { locator: Locator, } +/// A range of text from a single [`Buffer`], to be shown as an [`Excerpt`]. +/// These ranges are relative to the buffer itself #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExcerptRange { + /// The full range of text to be shown in the excerpt. pub context: Range, + /// The primary range of text to be highlighted in the excerpt. + /// In a multi-buffer search, this would be the text that matched the search pub primary: Option>, } #[derive(Clone, Debug, Default)] struct ExcerptSummary { excerpt_id: ExcerptId, + /// The location of the last [`Excerpt`] being summarized excerpt_locator: Locator, + /// The maximum row of the [`Excerpt`]s being summarized max_buffer_row: u32, text: TextSummary, } @@ -307,6 +336,7 @@ impl MultiBuffer { self.replica_id } + /// Returns an up-to-date snapshot of the MultiBuffer. pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot { self.sync(cx); self.snapshot.borrow().clone() diff --git a/crates/rope/src/point.rs b/crates/rope/src/point.rs index 4be01dfdac..60e6fb3a39 100644 --- a/crates/rope/src/point.rs +++ b/crates/rope/src/point.rs @@ -3,6 +3,7 @@ use std::{ ops::{Add, AddAssign, Sub}, }; +/// A zero-indexed point in a text buffer consisting of a row and column. #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)] pub struct Point { pub row: u32, diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 2c677d77b5..7bdc53d7c9 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -953,15 +953,24 @@ impl sum_tree::Summary for ChunkSummary { } } +/// Summary of a string of text. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct TextSummary { + /// Length in UTF-8 pub len: usize, + /// Length in UTF-16 code units pub len_utf16: OffsetUtf16, + /// A point representing the number of lines and the length of the last line pub lines: Point, + /// How many `char`s are in the first line pub first_line_chars: u32, + /// How many `char`s are in the last line pub last_line_chars: u32, + /// How many UTF-16 code units are in the last line pub last_line_len_utf16: u32, + /// The row idx of the longest row pub longest_row: u32, + /// How many `char`s are in the longest row pub longest_row_chars: u32, } diff --git a/crates/sum_tree/src/sum_tree.rs b/crates/sum_tree/src/sum_tree.rs index 5e2a8c8e38..c9e1ae5bcc 100644 --- a/crates/sum_tree/src/sum_tree.rs +++ b/crates/sum_tree/src/sum_tree.rs @@ -14,24 +14,39 @@ pub const TREE_BASE: usize = 2; #[cfg(not(test))] pub const TREE_BASE: usize = 6; +/// An item that can be stored in a [`SumTree`] +/// +/// Must be summarized by a type that implements [`Summary`] pub trait Item: Clone { type Summary: Summary; fn summary(&self) -> Self::Summary; } +/// An [`Item`] whose summary has a specific key that can be used to identify it pub trait KeyedItem: Item { type Key: for<'a> Dimension<'a, Self::Summary> + Ord; fn key(&self) -> Self::Key; } +/// A type that describes the Sum of all [`Item`]s in a subtree of the [`SumTree`] +/// +/// Each Summary type can have multiple [`Dimensions`] that it measures, +/// which can be used to navigate the tree pub trait Summary: Default + Clone + fmt::Debug { type Context; fn add_summary(&mut self, summary: &Self, cx: &Self::Context); } +/// Each [`Summary`] type can have more than one [`Dimension`] type that it measures. +/// +/// You can use dimensions to seek to a specific location in the [`SumTree`] +/// +/// # Example: +/// Zed's rope has a `TextSummary` type that summarizes lines, characters, and bytes. +/// Each of these are different dimensions we may want to seek to pub trait Dimension<'a, S: Summary>: Clone + fmt::Debug + Default { fn add_summary(&mut self, _summary: &'a S, _: &S::Context); @@ -97,10 +112,33 @@ impl fmt::Debug for End { } } +/// Bias is used to settle ambiguities when determining positions in an ordered sequence. +/// +/// The primary use case is for text, where Bias influences +/// which character an offset or anchor is associated with. +/// +/// # Examples +/// Given the buffer `AˇBCD`: +/// - The offset of the cursor is 1 +/// - [Bias::Left] would attach the cursor to the character `A` +/// - [Bias::Right] would attach the cursor to the character `B` +/// +/// Given the buffer `A«BCˇ»D`: +/// - The offset of the cursor is 3, and the selection is from 1 to 3 +/// - The left anchor of the selection has [Bias::Right], attaching it to the character `B` +/// - The right anchor of the selection has [Bias::Left], attaching it to the character `C` +/// +/// Given the buffer `{ˇ<...>`, where `<...>` is a folded region: +/// - The display offset of the cursor is 1, but the offset in the buffer is determined by the bias +/// - [Bias::Left] would attach the cursor to the character `{`, with a buffer offset of 1 +/// - [Bias::Right] would attach the cursor to the first character of the folded region, +/// and the buffer offset would be the offset of the first character of the folded region #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Default)] pub enum Bias { + /// Attach to the character on the left #[default] Left, + /// Attach to the character on the right Right, } @@ -113,6 +151,10 @@ impl Bias { } } +/// A B-tree where each leaf node contains an [`Item`] of type `T`, +/// and each internal node contains a [`Summary`] of the items in its subtree. +/// +/// Any [`Dimension`] supported by the [`Summary`] type can be used to seek to a specific location in the tree. #[derive(Debug, Clone)] pub struct SumTree(Arc>); diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index db44a34030..ceec293727 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -6,10 +6,13 @@ use anyhow::Result; use std::{cmp::Ordering, fmt::Debug, ops::Range}; use sum_tree::Bias; +/// A timestamped position in a buffer #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)] pub struct Anchor { pub timestamp: clock::Lamport, + /// The byte offset in the buffer pub offset: usize, + /// Describes which character the anchor is biased towards pub bias: Bias, pub buffer_id: Option, } diff --git a/crates/text/src/locator.rs b/crates/text/src/locator.rs index 0fb2fa5d16..ae5cb36070 100644 --- a/crates/text/src/locator.rs +++ b/crates/text/src/locator.rs @@ -7,6 +7,13 @@ lazy_static! { static ref MAX: Locator = Locator::max(); } +/// An identifier for a position in a ordered collection. +/// +/// Allows prepending and appending without needing to renumber existing locators +/// using `Locator::between(lhs, rhs)`. +/// +/// The initial location for a collection should be `Locator::between(Locator::min(), Locator::max())`, +/// leaving room for items to be inserted before and after it. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Locator(SmallVec<[u64; 4]>);