Max BrunsfeldWIP848df0a1 year agoHistory
4763 lines · 168.8 KB · Rust
RawDownload
use super::{    anchor::Anchor,    position::{TypedOffset, TypedPoint, TypedPointUtf16, TypedRow},    Event, ExcerptId, ExcerptRange, MultiBuffer,};use anyhow::{anyhow, Result};use collections::{BTreeMap, Bound, HashMap, HashSet};use futures::{channel::mpsc, SinkExt};use gpui::{AppContext, EventEmitter, Model, ModelContext, Task};use itertools::Itertools;use language::{    AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, Chunk,    CursorShape, DiagnosticEntry, DiskState, File, IndentGuide, IndentSize, OffsetRangeExt,    OffsetUtf16, Point, Selection, TextDimension, ToOffset as _, ToPoint as _, TransactionId,};use smallvec::SmallVec;use std::{    any::type_name,    borrow::Cow,    cell::{Ref, RefCell},    cmp, fmt,    future::Future,    io,    iter::{self, FromIterator},    mem,    ops::{Range, RangeBounds, Sub},    str,    sync::Arc,    time::{Duration, Instant},};use sum_tree::{Bias, Cursor, SumTree};use text::{locator::Locator, BufferId, Edit, Patch, TextSummary};use util::post_inc; pub type ExcerptOffset = TypedOffset<ExcerptMap>;pub type ExcerptPoint = TypedPoint<ExcerptMap>;pub type ExcerptPointUtf16 = TypedPointUtf16<ExcerptMap>;pub type ExcerptRow = TypedRow<ExcerptMap>; #[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 ExcerptMap {    /// A snapshot of the [`Excerpt`]s in the MultiBuffer.    /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot.    snapshot: RefCell<ExcerptMapSnapshot>,    /// Contains the state of the buffers being edited    buffers: RefCell<HashMap<BufferId, BufferState>>,    /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`]    pub(crate) singleton: bool,    history: History,    title: Option<String>,    capability: Capability,    edits: RefCell<Patch<ExcerptOffset>>,} /// 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 range within the buffer's diff base that this hunk corresponds to.    pub diff_base_byte_range: Range<usize>,} #[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);} #[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,} 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 ExcerptMapSnapshot {    singleton: bool,    excerpts: SumTree<Excerpt>,    excerpt_ids: SumTree<ExcerptIdMapping>,    trailing_excerpt_update_count: usize,    non_text_state_update_count: usize,    edit_count: usize,    is_dirty: bool,    has_deleted_file: bool,    has_conflict: bool,    show_headers: bool,} #[derive(Clone)]pub struct ExcerptInfo {    pub id: ExcerptId,    pub buffer: BufferSnapshot,    pub buffer_id: BufferId,    pub range: ExcerptRange<text::Anchor>,    pub text_summary: TextSummary,} 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: 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 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,    excerpt_offset: ExcerptOffset,    excerpt_position: ExcerptPoint,} #[derive(Clone, Debug)]struct ExcerptIdMapping {    id: ExcerptId,    locator: Locator,} #[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(Clone)]pub struct ExcerptMapRows<'a> {    buffer_row_range: Range<u32>,    excerpts: Cursor<'a, Excerpt, ExcerptPoint>,} pub struct ExcerptMapChunks<'a> {    range: Range<ExcerptOffset>,    excerpts: Cursor<'a, Excerpt, ExcerptOffset>,    excerpt_chunks: Option<ExcerptChunks<'a>>,    language_aware: bool,} pub struct ExcerptMapBytes<'a> {    range: Range<ExcerptOffset>,    excerpts: Cursor<'a, Excerpt, ExcerptOffset>,    excerpt_bytes: Option<ExcerptBytes<'a>>,    chunk: &'a [u8],} pub struct ReversedExcerptMapBytes<'a> {    range: Range<ExcerptOffset>,    excerpts: Cursor<'a, Excerpt, ExcerptOffset>,    excerpt_bytes: Option<ExcerptBytes<'a>>,    chunk: &'a [u8],} struct ExcerptChunks<'a> {    excerpt_id: ExcerptId,    content_chunks: BufferChunks<'a>,    footer_height: usize,} struct ExcerptBytes<'a> {    content_bytes: text::Bytes<'a>,    padding_height: usize,    reversed: bool,} struct BufferEdit {    range: Range<usize>,    new_text: Arc<str>,    is_insertion: bool,    original_indent_column: u32,} #[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 ExcerptMap {    pub fn new(capability: Capability) -> Self {        Self {            snapshot: RefCell::new(ExcerptMapSnapshot {                show_headers: true,                ..ExcerptMapSnapshot::default()            }),            buffers: RefCell::default(),            edits: Default::default(),            singleton: false,            capability,            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(capability: Capability) -> Self {        Self {            snapshot: Default::default(),            buffers: Default::default(),            edits: Default::default(),            singleton: false,            capability,            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<MultiBuffer>) -> 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),            singleton: self.singleton,            capability: self.capability,            history: self.history.clone(),            edits: self.edits.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<MultiBuffer>) -> Self {        let mut this = Self::new(buffer.read(cx).capability());        this.singleton = true;        this.push_excerpts(            buffer,            [ExcerptRange {                context: text::Anchor::MIN..text::Anchor::MAX,                primary: None,            }],            cx,        );        this.snapshot.borrow_mut().singleton = true;        this    }     /// Returns an up-to-date snapshot of the MultiBuffer.    pub fn snapshot(&self, cx: &AppContext) -> ExcerptMapSnapshot {        self.sync(cx);        self.snapshot.borrow().clone()    }     pub fn read(&self, cx: &AppContext) -> Ref<ExcerptMapSnapshot> {        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 is_dirty(&self, cx: &AppContext) -> bool {        self.read(cx).is_dirty()    }     pub fn has_deleted_file(&self, cx: &AppContext) -> bool {        self.read(cx).has_deleted_file()    }     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) -> ExcerptOffset {        self.read(cx).len()    }     pub fn is_empty(&self, cx: &AppContext) -> bool {        self.len(cx).value != 0    }     pub fn edit<I, T>(        &self,        edits: I,        autoindent_mode: Option<AutoindentMode>,        cx: &mut ModelContext<MultiBuffer>,    ) where        I: IntoIterator<Item = (Range<ExcerptOffset>, T)>,        T: Into<Arc<str>>,    {        let snapshot = self.read(cx);        let edits = edits            .into_iter()            .map(|(mut range, new_text)| {                if range.start > range.end {                    mem::swap(&mut range.start, &mut range.end);                }                (range, new_text.into())            })            .collect::<Vec<_>>();         return edit_internal(self, snapshot, edits, autoindent_mode, cx);         // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR.        fn edit_internal(            this: &ExcerptMap,            snapshot: Ref<ExcerptMapSnapshot>,            edits: Vec<(Range<ExcerptOffset>, Arc<str>)>,            mut autoindent_mode: Option<AutoindentMode>,            cx: &mut ModelContext<MultiBuffer>,        ) {            if this.read_only() || this.buffers.borrow().is_empty() {                return;            }             if let Some(buffer) = this.as_singleton() {                buffer.update(cx, |buffer, cx| {                    buffer.edit(                        edits                            .into_iter()                            .map(|(range, text)| (range.start.value..range.end.value, text)),                        autoindent_mode,                        cx,                    );                });                cx.emit(Event::ExcerptsEdited {                    ids: this.excerpt_ids(),                });                return;            }             let original_indent_columns = match &mut autoindent_mode {                Some(AutoindentMode::Block {                    original_indent_columns,                }) => mem::take(original_indent_columns),                _ => Default::default(),            };             let (buffer_edits, edited_excerpt_ids) =                this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns);            drop(snapshot); 

Lines 1–500 of 4763.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t