Antonio ScandurraWIP11de59e2 years agoHistory
6814 lines · 248.3 KB · Rust
RawDownload
mod anchor2; pub use anchor2::{Anchor, AnchorRangeExt, Offset};use anyhow::{anyhow, Result};use clock::ReplicaId;use collections::{BTreeMap, Bound, HashMap, HashSet};use futures::{channel::mpsc, SinkExt};use git::diff::DiffHunk;use gpui::{AppContext, EventEmitter, Model, ModelContext};use itertools::Itertools;use language::{    language_settings::{language_settings, LanguageSettings},    AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier,    CharKind, Chunk, CursorShape, DiagnosticEntry, File, IndentGuide, IndentSize, Language,    LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection,    TextDimension, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _,    TransactionId, Unclipped,};use smallvec::SmallVec;use std::{    any::type_name,    borrow::Cow,    cell::{Ref, RefCell},    cmp, fmt,    future::Future,    io,    iter::{self},    ops::{Range, RangeBounds, Sub},    path::PathBuf,    str,    sync::Arc,    time::{Duration, Instant},};use sum_tree::{Bias, Cursor, SumTree};use text::{    locator::Locator,    subscription::{Subscription, Topic},    BufferId, Edit, TextSummary,};use theme::SyntaxTheme; #[cfg(any(test, feature = "test-support"))]use gpui::Context; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as 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>>,    subscriptions: Topic,    /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`]    singleton: bool,    replica_id: ReplicaId,    history: History,    title: Option<String>,    capability: Capability,} #[derive(Clone, Debug, PartialEq, Eq)]pub enum Event {    Edited { singleton_buffer_edited: bool },    TransactionUndone { transaction_id: TransactionId },    Reloaded,    DiffBaseChanged,    DiffUpdated { buffer: Model<Buffer> },    LanguageChanged(BufferId),    CapabilityChanged,    Reparsed(BufferId),    Saved,    FileHandleChanged,    Closed,    Discarded,    DirtyChanged,    DiagnosticsUpdated,} pub type MultiBufferPoint = Point; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, serde::Deserialize)]#[serde(transparent)]pub struct MultiBufferRow(pub u32); impl MultiBufferRow {    pub const MIN: Self = Self(0);    pub const MAX: Self = Self(u32::MAX);} #[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: Model<Buffer>,    last_version: clock::Global,    last_non_text_state_update_count: usize,    excerpts: Vec<Locator>,    _subscriptions: [gpui::Subscription; 2],} /// The contents of a [`MultiBuffer`] at a single point in time.#[derive(Clone, Default)]pub struct MultiBufferSnapshot {    singleton: bool,    excerpts: SumTree<Excerpt>,    trailing_excerpt_update_count: usize,    non_text_state_update_count: usize,    edit_count: usize,    is_dirty: bool,    has_conflict: bool,    show_headers: bool,} pub struct ExcerptInfo {    pub buffer: BufferSnapshot,    pub buffer_id: BufferId,    pub range: ExcerptRange<text::Anchor>,} impl std::fmt::Debug for ExcerptInfo {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        f.debug_struct(type_name::<Self>())            .field("buffer_id", &self.buffer_id)            .field("range", &self.range)            .finish()    }} /// A boundary between [`Excerpt`]s in a [`MultiBuffer`]#[derive(Debug)]pub struct ExcerptBoundary {    pub prev: Option<ExcerptInfo>,    pub next: Option<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.as_ref()) {            (None, _) => true,            (Some(_), None) => false,            (Some(prev), Some(next)) => prev.buffer_id != next.buffer_id,        }    }} /// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].#[derive(Clone)]struct Excerpt {    /// 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,    excerpt_offset: usize,} /// 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<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: Option<Range<T>>,} #[derive(Clone, Debug, Default)]pub struct ExcerptSummary {    path: Option<PathBuf>,    buffer_id: Option<BufferId>,    /// The maximum row of the [`Excerpt`]s being summarized    max_buffer_row: MultiBufferRow,    text: TextSummary,} #[derive(Clone)]pub struct MultiBufferRows<'a> {    buffer_row_range: Range<u32>,    excerpts: Cursor<'a, Excerpt, Point>,} pub struct MultiBufferChunks<'a> {    range: Range<usize>,    excerpts: Cursor<'a, Excerpt, usize>,    excerpt_chunks: Option<ExcerptChunks<'a>>,    language_aware: bool,} pub struct MultiBufferBytes<'a> {    range: Range<usize>,    excerpts: Cursor<'a, Excerpt, usize>,    excerpt_bytes: Option<ExcerptBytes<'a>>,    chunk: &'a [u8],} pub struct ReversedMultiBufferBytes<'a> {    range: Range<usize>,    excerpts: Cursor<'a, Excerpt, usize>,    excerpt_bytes: Option<ExcerptBytes<'a>>,    chunk: &'a [u8],} struct ExcerptChunks<'a> {    content_chunks: BufferChunks<'a>,    footer_height: usize,} struct ExcerptBytes<'a> {    content_bytes: text::Bytes<'a>,    padding_height: usize,    reversed: bool,} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum ExpandExcerptDirection {    Up,    Down,    UpAndDown,} impl ExpandExcerptDirection {    pub fn should_expand_up(&self) -> bool {        match self {            ExpandExcerptDirection::Up => true,            ExpandExcerptDirection::Down => false,            ExpandExcerptDirection::UpAndDown => true,        }    }     pub fn should_expand_down(&self) -> bool {        match self {            ExpandExcerptDirection::Up => false,            ExpandExcerptDirection::Down => true,            ExpandExcerptDirection::UpAndDown => true,        }    }} #[derive(Clone, Debug, PartialEq)]pub struct MultiBufferIndentGuide {    pub multibuffer_row_range: Range<MultiBufferRow>,    pub buffer: IndentGuide,} impl std::ops::Deref for MultiBufferIndentGuide {    type Target = IndentGuide;     fn deref(&self) -> &Self::Target {        &self.buffer    }} impl MultiBuffer {    pub fn new(replica_id: ReplicaId, capability: Capability) -> Self {        Self {            snapshot: RefCell::new(MultiBufferSnapshot {                show_headers: true,                ..MultiBufferSnapshot::default()            }),            buffers: RefCell::default(),            subscriptions: Topic::default(),            singleton: false,            capability,            replica_id,            title: None,            history: History {                next_transaction_id: clock::Lamport::default(),                undo_stack: Vec::new(),                redo_stack: Vec::new(),                transaction_depth: 0,                group_interval: Duration::from_millis(300),            },        }    }     pub fn without_headers(replica_id: ReplicaId, capability: Capability) -> Self {        Self {            snapshot: Default::default(),            buffers: Default::default(),            subscriptions: Default::default(),            singleton: false,            capability,            replica_id,            history: History {                next_transaction_id: Default::default(),                undo_stack: Default::default(),                redo_stack: Default::default(),                transaction_depth: 0,                group_interval: Duration::from_millis(300),            },            title: Default::default(),        }    }     pub fn clone(&self, new_cx: &mut ModelContext<Self>) -> Self {        let mut buffers = HashMap::default();        for (buffer_id, buffer_state) in self.buffers.borrow().iter() {            buffers.insert(                *buffer_id,                BufferState {                    buffer: buffer_state.buffer.clone(),                    last_version: buffer_state.last_version.clone(),                    last_non_text_state_update_count: buffer_state.last_non_text_state_update_count,                    excerpts: buffer_state.excerpts.clone(),                    _subscriptions: [                        new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),                        new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event),                    ],                },            );        }        Self {            snapshot: RefCell::new(self.snapshot.borrow().clone()),            buffers: RefCell::new(buffers),            subscriptions: Default::default(),            singleton: self.singleton,            capability: self.capability,            replica_id: self.replica_id,            history: self.history.clone(),            title: self.title.clone(),        }    }     pub fn with_title(mut self, title: String) -> Self {        self.title = Some(title);        self    }     pub fn read_only(&self) -> bool {        self.capability == Capability::ReadOnly    }     pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {        let mut this = Self::new(buffer.read(cx).replica_id(), buffer.read(cx).capability());        this.singleton = true;        this.insert_excerpts(            buffer,            [ExcerptRange {                context: text::Anchor::MIN..text::Anchor::MAX,                primary: None,            }],            cx,        );        this.snapshot.borrow_mut().singleton = true;        this    }     pub fn replica_id(&self) -> ReplicaId {        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()    }     pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {        self.sync(cx);        self.snapshot.borrow()    }     pub fn as_singleton(&self) -> Option<Model<Buffer>> {        if self.singleton {            return Some(                self.buffers                    .borrow()                    .values()                    .next()                    .unwrap()                    .buffer                    .clone(),            );        } else {            None        }    }     pub fn is_singleton(&self) -> bool {        self.singleton    }     pub fn subscribe(&mut self) -> Subscription {        self.subscriptions.subscribe()    }     pub fn is_dirty(&self, cx: &AppContext) -> bool {        self.read(cx).is_dirty()    }     pub fn has_conflict(&self, cx: &AppContext) -> bool {        self.read(cx).has_conflict()    }     // The `is_empty` signature doesn't match what clippy expects    #[allow(clippy::len_without_is_empty)]    pub fn len(&self, cx: &AppContext) -> usize {        self.read(cx).len()    }     pub fn is_empty(&self, cx: &AppContext) -> bool {        self.len(cx) != 0    }     pub fn symbols_containing<T: ToOffset>(        &self,        offset: T,        theme: Option<&SyntaxTheme>,        cx: &AppContext,    ) -> Option<(BufferId, Vec<OutlineItem<Anchor>>)> {        self.read(cx).symbols_containing(offset, theme)    }     pub fn edit<I, S, T>(        &mut self,        edits: I,        mut autoindent_mode: Option<AutoindentMode>,        cx: &mut ModelContext<Self>,    ) where        I: IntoIterator<Item = (Range<S>, T)>,        S: ToOffset,        T: Into<Arc<str>>,    {        todo!()        //     if self.read_only() {        //         return;        //     }        //     if self.buffers.borrow().is_empty() {        //         return;        //     }         //     let snapshot = self.read(cx);        //     let edits = edits.into_iter().map(|(range, new_text)| {        //         let mut range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot);        //         if range.start > range.end {        //             mem::swap(&mut range.start, &mut range.end);        //         }        //         (range, new_text)        //     });         //     if let Some(buffer) = self.as_singleton() {        //         buffer.update(cx, |buffer, cx| {        //             buffer.edit(edits, autoindent_mode, cx);        //         });        //         // todo!()        //         // cx.emit(Event::ExcerptsEdited {        //         //     ids: self.excerpt_ids(),

Lines 1–500 of 6814.