7969 lines · 285.2 KB · Rust
RawDownload
mod anchor;#[cfg(test)]mod multi_buffer_tests;mod position; pub use anchor::{Anchor, AnchorRangeExt, Offset};pub use position::{TypedOffset, TypedPoint, TypedRow}; use anyhow::{Result, anyhow};use buffer_diff::{    BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkSecondaryStatus, DiffHunkStatus,    DiffHunkStatusKind,};use clock::ReplicaId;use collections::{BTreeMap, Bound, HashMap, HashSet};use gpui::{App, AppContext as _, Context, Entity, EntityId, EventEmitter, Task};use itertools::Itertools;use language::{    AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier,    CharKind, Chunk, CursorShape, DiagnosticEntry, DiskState, File, IndentSize, Language,    LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection,    TextDimension, TextObject, ToOffset as _, ToPoint as _, TransactionId, TreeSitterOptions,    Unclipped,    language_settings::{IndentGuideSettings, LanguageSettings, language_settings},}; use rope::DimensionPair;use smallvec::SmallVec;use smol::future::yield_now;use std::{    any::type_name,    borrow::Cow,    cell::{Cell, Ref, RefCell},    cmp, fmt,    future::Future,    io,    iter::{self, FromIterator},    mem,    ops::{Range, RangeBounds, Sub},    path::{Path, PathBuf},    rc::Rc,    str,    sync::Arc,    time::{Duration, Instant},};use sum_tree::{Bias, Cursor, Dimension, SumTree, Summary, TreeMap};use text::{    BufferId, Edit, LineIndent, TextSummary,    locator::Locator,    subscription::{Subscription, Topic},};use theme::SyntaxTheme;use util::post_inc; 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 <https://zed.dev/features#multi-buffers>pub struct MultiBuffer {    /// A snapshot of the [`Excerpt`]s in the MultiBuffer.    /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot.    snapshot: RefCell<MultiBufferSnapshot>,    /// Contains the state of the buffers being edited    buffers: RefCell<HashMap<BufferId, BufferState>>,    // only used by consumers using `set_excerpts_for_buffer`    excerpts_by_path: BTreeMap<PathKey, Vec<ExcerptId>>,    paths_by_excerpt: HashMap<ExcerptId, PathKey>,    diffs: HashMap<BufferId, DiffState>,    // all_diff_hunks_expanded: bool,    subscriptions: Topic,    /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`]    singleton: bool,    history: History,    title: Option<String>,    capability: Capability,    buffer_changed_since_sync: Rc<Cell<bool>>,} #[derive(Clone, Debug, PartialEq, Eq)]pub enum MultiOrSingleBufferOffsetRange {    Single(Range<usize>),    Multi(Range<usize>),} #[derive(Clone, Debug, PartialEq, Eq)]pub enum Event {    ExcerptsAdded {        buffer: Entity<Buffer>,        predecessor: ExcerptId,        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,    },    ExcerptsRemoved {        ids: Vec<ExcerptId>,        removed_buffer_ids: Vec<BufferId>,    },    ExcerptsExpanded {        ids: Vec<ExcerptId>,    },    ExcerptsEdited {        excerpt_ids: Vec<ExcerptId>,        buffer_ids: Vec<BufferId>,    },    DiffHunksToggled,    Edited {        singleton_buffer_edited: bool,        edited_buffer: Option<Entity<Buffer>>,    },    TransactionUndone {        transaction_id: TransactionId,    },    Reloaded,    ReloadNeeded,     LanguageChanged(BufferId),    CapabilityChanged,    Reparsed(BufferId),    Saved,    FileHandleChanged,    Closed,    Discarded,    DirtyChanged,    DiagnosticsUpdated,    BufferDiffChanged,} /// A diff hunk, representing a range of consequent lines in a multibuffer.#[derive(Debug, Clone, PartialEq, Eq)]pub struct MultiBufferDiffHunk {    /// The row range in the multibuffer where this diff hunk appears.    pub row_range: Range<MultiBufferRow>,    /// The buffer ID that this hunk belongs to.    pub buffer_id: BufferId,    /// The range of the underlying buffer that this hunk corresponds to.    pub buffer_range: Range<text::Anchor>,    /// The excerpt that contains the diff hunk.    pub excerpt_id: ExcerptId,    /// The range within the buffer's diff base that this hunk corresponds to.    pub diff_base_byte_range: Range<usize>,    /// Whether or not this hunk also appears in the 'secondary diff'.    pub secondary_status: DiffHunkSecondaryStatus,} impl MultiBufferDiffHunk {    pub fn status(&self) -> DiffHunkStatus {        let kind = if self.buffer_range.start == self.buffer_range.end {            DiffHunkStatusKind::Deleted        } else if self.diff_base_byte_range.is_empty() {            DiffHunkStatusKind::Added        } else {            DiffHunkStatusKind::Modified        };        DiffHunkStatus {            kind,            secondary: self.secondary_status,        }    }     pub fn is_created_file(&self) -> bool {        self.diff_base_byte_range == (0..0)            && self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX)    }     pub fn multi_buffer_range(&self) -> Range<Anchor> {        let start = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.start);        let end = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.end);        start..end    }} #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]pub struct PathKey {    namespace: u32,    path: Arc<Path>,} impl PathKey {    pub fn namespaced(namespace: u32, path: Arc<Path>) -> Self {        Self { namespace, path }    }     pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {        if let Some(file) = buffer.read(cx).file() {            Self::namespaced(1, Arc::from(file.full_path(cx)))        } else {            Self::namespaced(0, Arc::from(PathBuf::from(buffer.entity_id().to_string())))        }    }     pub fn path(&self) -> &Arc<Path> {        &self.path    }} pub type MultiBufferPoint = Point;type ExcerptOffset = TypedOffset<Excerpt>;type ExcerptPoint = TypedPoint<Excerpt>; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]#[serde(transparent)]pub struct MultiBufferRow(pub u32); impl MultiBufferRow {    pub const MIN: Self = Self(0);    pub const MAX: Self = Self(u32::MAX);} impl std::ops::Add<usize> for MultiBufferRow {    type Output = Self;     fn add(self, rhs: usize) -> Self::Output {        MultiBufferRow(self.0 + rhs as u32)    }} #[derive(Clone)]struct History {    next_transaction_id: TransactionId,    undo_stack: Vec<Transaction>,    redo_stack: Vec<Transaction>,    transaction_depth: usize,    group_interval: Duration,} #[derive(Clone)]struct Transaction {    id: TransactionId,    buffer_transactions: HashMap<BufferId, text::TransactionId>,    first_edit_at: Instant,    last_edit_at: Instant,    suppress_grouping: bool,} pub trait ToOffset: 'static + fmt::Debug {    fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;} pub trait ToOffsetUtf16: 'static + fmt::Debug {    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16;} pub trait ToPoint: 'static + fmt::Debug {    fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;} pub trait ToPointUtf16: 'static + fmt::Debug {    fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;} struct BufferState {    buffer: Entity<Buffer>,    last_version: clock::Global,    last_non_text_state_update_count: usize,    excerpts: Vec<Locator>,    _subscriptions: [gpui::Subscription; 2],} struct DiffState {    diff: Entity<BufferDiff>,    _subscription: gpui::Subscription,} impl DiffState {    fn new(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self {        DiffState {            _subscription: cx.subscribe(&diff, |this, diff, event, cx| match event {                BufferDiffEvent::DiffChanged { changed_range } => {                    if let Some(changed_range) = changed_range.clone() {                        this.buffer_diff_changed(diff, changed_range, cx)                    }                    cx.emit(Event::BufferDiffChanged);                }                BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),                _ => {}            }),            diff,        }    }} /// The contents of a [`MultiBuffer`] at a single point in time.#[derive(Clone, Default)]pub struct MultiBufferSnapshot {    singleton: bool,    excerpts: SumTree<Excerpt>,    excerpt_ids: SumTree<ExcerptIdMapping>,    diffs: TreeMap<BufferId, BufferDiffSnapshot>,    diff_transforms: SumTree<DiffTransform>,    replaced_excerpts: TreeMap<ExcerptId, ExcerptId>,    trailing_excerpt_update_count: usize,    all_diff_hunks_expanded: bool,    non_text_state_update_count: usize,    edit_count: usize,    is_dirty: bool,    has_deleted_file: bool,    has_conflict: bool,    show_headers: bool,} #[derive(Debug, Clone)]enum DiffTransform {    BufferContent {        summary: TextSummary,        inserted_hunk_info: Option<DiffTransformHunkInfo>,    },    DeletedHunk {        summary: TextSummary,        buffer_id: BufferId,        hunk_info: DiffTransformHunkInfo,        base_text_byte_range: Range<usize>,        has_trailing_newline: bool,    },} #[derive(Clone, Copy, Debug)]struct DiffTransformHunkInfo {    excerpt_id: ExcerptId,    hunk_start_anchor: text::Anchor,    hunk_secondary_status: DiffHunkSecondaryStatus,} impl Eq for DiffTransformHunkInfo {} impl PartialEq for DiffTransformHunkInfo {    fn eq(&self, other: &DiffTransformHunkInfo) -> bool {        self.excerpt_id == other.excerpt_id && self.hunk_start_anchor == other.hunk_start_anchor    }} impl std::hash::Hash for DiffTransformHunkInfo {    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {        self.excerpt_id.hash(state);        self.hunk_start_anchor.hash(state);    }} #[derive(Clone)]pub struct ExcerptInfo {    pub id: ExcerptId,    pub buffer: BufferSnapshot,    pub buffer_id: BufferId,    pub range: ExcerptRange<text::Anchor>,    pub end_row: MultiBufferRow,} impl std::fmt::Debug for ExcerptInfo {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        f.debug_struct(type_name::<Self>())            .field("id", &self.id)            .field("buffer_id", &self.buffer_id)            .field("path", &self.buffer.file().map(|f| f.path()))            .field("range", &self.range)            .finish()    }} /// A boundary between `Excerpt`s in a [`MultiBuffer`]#[derive(Debug)]pub struct ExcerptBoundary {    pub prev: Option<ExcerptInfo>,    pub next: ExcerptInfo,    /// The row in the `MultiBuffer` where the boundary is located    pub row: MultiBufferRow,} impl ExcerptBoundary {    pub fn starts_new_buffer(&self) -> bool {        match (self.prev.as_ref(), &self.next) {            (None, _) => true,            (Some(prev), next) => prev.buffer_id != next.buffer_id,        }    }} #[derive(Copy, Clone, Debug, PartialEq, Eq)]pub struct ExpandInfo {    pub direction: ExpandExcerptDirection,    pub excerpt_id: ExcerptId,} #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]pub struct RowInfo {    pub buffer_id: Option<BufferId>,    pub buffer_row: Option<u32>,    pub multibuffer_row: Option<MultiBufferRow>,    pub diff_status: Option<buffer_diff::DiffHunkStatus>,    pub expand_info: Option<ExpandInfo>,} /// 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<text::Anchor>,    /// The last row in the excerpted slice of the buffer    max_buffer_row: BufferRow,    /// A summary of the text in the excerpt    text_summary: TextSummary,    has_trailing_newline: bool,} /// A public view into an `Excerpt` in a [`MultiBuffer`].////// Contains methods for getting the [`Buffer`] of the excerpt,/// as well as mapping offsets to/from buffer and multibuffer coordinates.#[derive(Clone)]pub struct MultiBufferExcerpt<'a> {    excerpt: &'a Excerpt,    diff_transforms: sum_tree::Cursor<'a, DiffTransform, DiffTransforms<usize>>,    offset: usize,    excerpt_offset: ExcerptDimension<usize>,    buffer_offset: usize,} #[derive(Clone, Debug)]struct ExcerptIdMapping {    id: ExcerptId,    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, Hash)]pub struct ExcerptRange<T> {    /// The full range of text to be shown in the excerpt.    pub context: Range<T>,    /// 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: Range<T>,} impl<T: Clone> ExcerptRange<T> {    pub fn new(context: Range<T>) -> Self {        Self {            context: context.clone(),            primary: context,        }    }} #[derive(Clone, Debug, Default)]pub struct ExcerptSummary {    excerpt_id: ExcerptId,    /// The location of the last [`Excerpt`] being summarized    excerpt_locator: Locator,    widest_line_number: u32,    text: TextSummary,} #[derive(Debug, Clone)]pub struct DiffTransformSummary {    input: TextSummary,    output: TextSummary,} #[derive(Clone)]pub struct MultiBufferRows<'a> {    point: Point,    is_empty: bool,    is_singleton: bool,    cursor: MultiBufferCursor<'a, Point>,} pub struct MultiBufferChunks<'a> {    excerpts: Cursor<'a, Excerpt, ExcerptOffset>,    diff_transforms: Cursor<'a, DiffTransform, (usize, ExcerptOffset)>,    diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,    diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>,    buffer_chunk: Option<Chunk<'a>>,    range: Range<usize>,    excerpt_offset_range: Range<ExcerptOffset>,    excerpt_chunks: Option<ExcerptChunks<'a>>,    language_aware: bool,} pub struct ReversedMultiBufferChunks<'a> {    cursor: MultiBufferCursor<'a, usize>,    current_chunks: Option<rope::Chunks<'a>>,    start: usize,    offset: usize,} pub struct MultiBufferBytes<'a> {    range: Range<usize>,    cursor: MultiBufferCursor<'a, usize>,    excerpt_bytes: Option<text::Bytes<'a>>,    has_trailing_newline: bool,    chunk: &'a [u8],}

Lines 1–500 of 7969.