Conrad Irwinfix merge conflicts7adcf901 year agoHistory
2562 lines · 86.1 KB · Rust
RawDownload
use super::{custom_highlights::CustomHighlightsChunks, DisplayAnchor};use crate::RowInfo;use collections::HashMap;use git::diff::DiffHunkStatus;use gpui::{AppContext, Context as _, HighlightStyle, Model, ModelContext, Subscription};use language::{BufferChunks, BufferId, Chunk};use multi_buffer::{    Anchor, AnchorRangeExt, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow, MultiBufferRows,    MultiBufferSnapshot, ToOffset, ToPoint as _,};use project::buffer_store::BufferChangeSet;use std::{    any::TypeId,    mem::{self},    ops::Range,    sync::Arc,};use sum_tree::{Cursor, SumTree, TreeMap};use text::{Bias, Edit, Patch, Point, TextSummary, ToOffset as _, ToPoint as _};use util::debug_panic; pub(crate) struct DiffMap {    snapshot: DiffMapSnapshot,    multibuffer: Model<MultiBuffer>,    diff_bases: HashMap<BufferId, ChangeSetState>,    all_hunks_expanded: bool,    edits_since_sync: Patch<DiffOffset>,} struct ChangeSetState {    change_set: Model<BufferChangeSet>,    _subscription: Subscription,} #[derive(Clone)]struct DiffSnapshot {    diff: git::diff::BufferDiff,    base_text: language::BufferSnapshot,    base_text_version: usize,} #[derive(Clone)]pub struct DiffMapSnapshot {    diffs: TreeMap<BufferId, DiffSnapshot>,    pub(crate) transforms: SumTree<DiffTransform>,    pub(crate) version: usize,    pub(crate) buffer: MultiBufferSnapshot,} #[derive(Debug, Clone)]pub(crate) enum DiffTransform {    BufferContent {        summary: TextSummary,        is_inserted_hunk: bool,    },    DeletedHunk {        summary_including_newline: TextSummary,        buffer_id: BufferId,        base_text_byte_range: Range<usize>,        base_text_start: Point,        needs_newline: bool,    },} #[derive(Debug, Clone)]pub(crate) struct DiffTransformSummary {    multibuffer_map: TextSummary,    diff_map: TextSummary,} impl DiffTransformSummary {    pub fn multibuffer_point(&self) -> Point {        self.multibuffer_map.lines    }    pub fn multibuffer_offset(&self) -> usize {        self.multibuffer_map.len    }    pub fn diff_point(&self) -> DiffPoint {        DiffPoint(self.diff_map.lines)    }    pub fn diff_offset(&self) -> DiffOffset {        DiffOffset(self.diff_map.len)    }} pub struct DiffMapChunks<'a> {    snapshot: &'a DiffMapSnapshot,    language_aware: bool,    cursor: Cursor<'a, DiffTransform, (DiffOffset, usize)>,    multibuffer_chunks: CustomHighlightsChunks<'a>,    multibuffer_chunk: Option<Chunk<'a>>,    multibuffer_offset: usize,    offset: DiffOffset,    end_offset: DiffOffset,    diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>,} #[derive(Clone)]pub struct DiffMapRows<'a> {    cursor: Cursor<'a, DiffTransform, (DiffPoint, Point)>,    diff_point: DiffPoint,    input_buffer_rows: MultiBufferRows<'a>,} pub type DiffEdit = text::Edit<DiffOffset>; #[derive(Debug)]enum ChangeKind {    DiffUpdated { base_changed: bool },    InputEdited,    ExpandOrCollapseHunks { range: Range<usize>, expand: bool },} #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct DiffOffset(pub usize); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct DiffPoint(pub Point); impl DiffPoint {    pub fn new(row: u32, col: u32) -> Self {        DiffPoint(Point::new(row, col))    }     pub fn row(&self) -> u32 {        self.0.row    }    pub fn column(&self) -> u32 {        self.0.column    }} impl DiffMap {    pub fn new(        multibuffer: Model<MultiBuffer>,        cx: &mut AppContext,    ) -> (Model<Self>, DiffMapSnapshot) {        let multibuffer_snapshot = multibuffer.read(cx).snapshot(cx);        let snapshot = DiffMapSnapshot {            diffs: TreeMap::default(),            version: 0,            transforms: SumTree::from_item(                DiffTransform::BufferContent {                    summary: multibuffer_snapshot.text_summary(),                    is_inserted_hunk: false,                },                &(),            ),            buffer: multibuffer_snapshot,        };         let this = cx.new_model(|_| Self {            multibuffer,            snapshot: snapshot.clone(),            all_hunks_expanded: false,            diff_bases: HashMap::default(),            edits_since_sync: Patch::default(),        });         (this, snapshot)    }     pub fn add_change_set(        &mut self,        change_set: Model<BufferChangeSet>,        cx: &mut ModelContext<Self>,    ) {        let buffer_id = change_set.read(cx).buffer_id;        self.buffer_diff_changed(change_set.clone(), cx);        self.diff_bases.insert(            buffer_id,            ChangeSetState {                _subscription: cx.observe(&change_set, Self::buffer_diff_changed),                change_set,            },        );    }     pub fn diff_base_for(&self, buffer_id: BufferId) -> Option<&Model<BufferChangeSet>> {        self.diff_bases            .get(&buffer_id)            .map(|state| &state.change_set)    }     pub fn sync(        &mut self,        multibuffer_snapshot: MultiBufferSnapshot,        buffer_edits: Vec<text::Edit<usize>>,        cx: &mut ModelContext<Self>,    ) -> (DiffMapSnapshot, Vec<DiffEdit>) {        let changes = buffer_edits            .iter()            .map(|edit| (edit.clone(), ChangeKind::InputEdited))            .collect::<Vec<_>>();         self.snapshot.buffer = multibuffer_snapshot.clone();        self.recompute_transforms(changes, cx);         (            self.snapshot.clone(),            mem::take(&mut self.edits_since_sync).into_inner(),        )    }     fn buffer_diff_changed(        &mut self,        change_set: Model<BufferChangeSet>,        cx: &mut ModelContext<Self>,    ) {        let change_set = change_set.read(cx);        let buffer_id = change_set.buffer_id;        let diff = change_set.diff_to_buffer.clone();        let base_text = change_set            .base_text            .as_ref()            .map(|buffer| buffer.read(cx).snapshot());        let base_text_version_changed = self            .snapshot            .diffs            .get(&buffer_id)            .map_or(true, |snapshot| {                snapshot.base_text_version != change_set.base_text_version            });         if let Some(base_text) = base_text.clone() {            self.snapshot.diffs.insert(                buffer_id,                DiffSnapshot {                    diff: diff.clone(),                    base_text,                    base_text_version: change_set.base_text_version,                },            );        } else {            self.snapshot.diffs.remove(&buffer_id);        }         let multibuffer = self.multibuffer.read(cx);        let multibuffer_snapshot = self.snapshot.buffer();        let changes = multibuffer            .ranges_for_buffer(buffer_id, cx)            .into_iter()            .map(|(_, range, _)| {                let multibuffer_start = multibuffer_snapshot.point_to_offset(range.start);                let multibuffer_end = multibuffer_snapshot.point_to_offset(range.end);                (                    text::Edit {                        old: multibuffer_start..multibuffer_end,                        new: multibuffer_start..multibuffer_end,                    },                    ChangeKind::DiffUpdated {                        base_changed: base_text_version_changed,                    },                )            })            .collect();        self.recompute_transforms(changes, cx);    }     pub(super) fn has_multiple_hunks(&self) -> bool {        self.snapshot            .diff_hunks_in_range(Anchor::min()..Anchor::max())            .nth(1)            .is_some()    }     pub(super) fn has_expanded_diff_hunks_in_ranges(        &self,        ranges: &[Range<multi_buffer::Anchor>],    ) -> bool {        let mut cursor = self.snapshot.transforms.cursor::<usize>(&());        let multibuffer_snapshot = self.snapshot.buffer();        for range in ranges {            let range = range.to_point(multibuffer_snapshot);            let start = multibuffer_snapshot.point_to_offset(Point::new(range.start.row, 0));            let end = multibuffer_snapshot.point_to_offset(Point::new(range.end.row + 1, 0));            let start = start.saturating_sub(1);            let end = multibuffer_snapshot.len().min(end + 1);            cursor.seek(&start, Bias::Right, &());            while *cursor.start() < end {                match cursor.item() {                    Some(DiffTransform::DeletedHunk { .. })                    | Some(DiffTransform::BufferContent {                        is_inserted_hunk: true,                        ..                    }) => return true,                    _ => {}                }                cursor.next(&());            }        }        false    }     pub(super) fn expand_diff_hunks(        &mut self,        ranges: Vec<Range<multi_buffer::Anchor>>,        cx: &mut ModelContext<Self>,    ) {        self.expand_or_collapse_diff_hunks(ranges, true, cx);    }     pub(super) fn collapse_diff_hunks(        &mut self,        ranges: Vec<Range<multi_buffer::Anchor>>,        cx: &mut ModelContext<Self>,    ) {        self.expand_or_collapse_diff_hunks(ranges, false, cx);    }     pub(super) fn set_all_hunks_expanded(&mut self, cx: &mut ModelContext<Self>) {        self.all_hunks_expanded = true;        self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], true, cx);    }     fn expand_or_collapse_diff_hunks(        &mut self,        ranges: Vec<Range<multi_buffer::Anchor>>,        expand: bool,        cx: &mut ModelContext<Self>,    ) {        let multibuffer_snapshot = self.snapshot.buffer();        let mut changes = Vec::new();        for range in ranges.iter() {            let multibuffer_start = range.start.to_point(multibuffer_snapshot);            let multibuffer_end = range.end.to_point(multibuffer_snapshot);            let multibuffer_start =                multibuffer_snapshot.point_to_offset(Point::new(multibuffer_start.row, 0));            let multibuffer_end =                multibuffer_snapshot.point_to_offset(Point::new(multibuffer_end.row + 1, 0));            let expanded_start = multibuffer_start.saturating_sub(1);            let expanded_end = multibuffer_snapshot.len().min(multibuffer_end);            changes.push((                text::Edit {                    old: expanded_start..expanded_end,                    new: expanded_start..expanded_end,                },                ChangeKind::ExpandOrCollapseHunks {                    range: multibuffer_start..multibuffer_end,                    expand,                },            ));        }         self.recompute_transforms(changes, cx);    }     fn recompute_transforms(        &mut self,        changes: Vec<(text::Edit<usize>, ChangeKind)>,        cx: &mut ModelContext<DiffMap>,    ) {        let multibuffer = self.multibuffer.read(cx);        let multibuffer_snapshot = self.snapshot.buffer();         let mut cursor = self.snapshot.transforms.cursor::<(usize, DiffOffset)>(&());        let mut new_transforms = SumTree::default();        let mut edits = Patch::default();         let mut changes = changes.into_iter().peekable();        let mut delta = 0_isize;        while let Some((mut edit, mut operation)) = changes.next() {            let mut to_skip = cursor.slice(&edit.old.start, Bias::Left, &());            while cursor.end(&()).0 < edit.old.start                || (cursor.end(&()).0 == edit.old.start && cursor.start().0 < edit.old.start)            {                to_skip.extend(cursor.item().cloned(), &());                cursor.next(&());            }             self.append_transforms(&mut new_transforms, to_skip);             let mut end_of_current_insert = 0;            loop {                let multibuffer_range = edit.new.clone();                let edit_old_start =                    cursor.start().1 + DiffOffset(edit.old.start - cursor.start().0);                let mut edit_old_end =                    cursor.start().1 + DiffOffset(edit.old.end - cursor.start().0);                 for (buffer, buffer_range, excerpt_id) in                    multibuffer.range_to_buffer_ranges(multibuffer_range.clone(), cx)                {                    let excerpt_range = multibuffer_snapshot                        .range_for_excerpt::<usize>(excerpt_id)                        .unwrap();                    let excerpt_buffer_range = multibuffer_snapshot                        .buffer_range_for_excerpt(excerpt_id)                        .unwrap();                    let buffer_id = buffer.read(cx).remote_id();                    let diff_state = self.snapshot.diffs.get(&buffer_id);                     let buffer = buffer.read(cx);                    let buffer_anchor_range = buffer.anchor_before(buffer_range.start)                        ..buffer.anchor_after(buffer_range.end);                    let change_start_buffer_offset = buffer_range.start;                    if let Some(diff_state) = diff_state {                        let diff = &diff_state.diff;                        let base_text = &diff_state.base_text;                         for hunk in diff.hunks_intersecting_range(buffer_anchor_range, buffer) {                            let hunk_start_buffer_offset =                                hunk.buffer_range.start.to_offset(buffer);                            let hunk_end_buffer_offset = hunk.buffer_range.end.to_offset(buffer);                             let excerpt_buffer_range_start_offset =                                excerpt_buffer_range.start.to_offset(buffer);                            let hunk_start_multibuffer_offset = excerpt_range.start                                + hunk_start_buffer_offset                                    .saturating_sub(excerpt_buffer_range_start_offset);                             self.push_buffer_content_transform(                                &mut new_transforms,                                hunk_start_multibuffer_offset,                                end_of_current_insert,                            );                             while cursor.end(&()).0 < hunk_start_multibuffer_offset                                || (cursor.end(&()).0 == hunk_start_multibuffer_offset                                    && cursor.start().0 < hunk_start_multibuffer_offset)                            {                                let Some(item) = cursor.item() else {                                    break;                                };                                if let DiffTransform::DeletedHunk { .. } = item {                                    let old_range = cursor.start().1..cursor.end(&()).1;                                    let new_offset =                                        DiffOffset((old_range.start.0 as isize + delta) as usize);                                    delta -= (old_range.end - old_range.start).0 as isize;                                    let edit = Edit {                                        old: old_range,                                        new: new_offset..new_offset,                                    };                                    edits.push(edit);                                }                                cursor.next(&());                            }                             let mut was_previously_expanded = false;                            let mut previous_expanded_summary = TextSummary::default();                            if cursor.start().0 == hunk_start_multibuffer_offset {                                match cursor.item() {                                    Some(DiffTransform::DeletedHunk {                                        summary_including_newline,                                        ..                                    }) => {                                        was_previously_expanded = true;                                        previous_expanded_summary =                                            summary_including_newline.clone();                                    }                                    Some(DiffTransform::BufferContent {                                        is_inserted_hunk, ..                                    }) => {                                        was_previously_expanded = *is_inserted_hunk;                                    }                                    None => {}                                };                            }                             let hunk_is_deletion =                                hunk_start_buffer_offset == hunk_end_buffer_offset;                             let should_expand_hunk = match &operation {                                ChangeKind::DiffUpdated { base_changed: true } => {                                    self.all_hunks_expanded                                }                                ChangeKind::ExpandOrCollapseHunks { range, expand } => {                                    let intersects = hunk_is_deletion                                        || (hunk_start_buffer_offset < range.end                                            && hunk_end_buffer_offset > range.start);                                    if *expand {                                        was_previously_expanded                                            || self.all_hunks_expanded                                            || intersects                                    } else {                                        !intersects                                            && (was_previously_expanded || self.all_hunks_expanded)                                    }                                }                                _ => was_previously_expanded || self.all_hunks_expanded,                            };                             if should_expand_hunk {                                if hunk.diff_base_byte_range.len() > 0                                    && hunk_start_buffer_offset >= change_start_buffer_offset                                {                                    let mut text_cursor = base_text.as_rope().cursor(0);                                    let base_text_start = text_cursor                                        .summary::<Point>(hunk.diff_base_byte_range.start);                                    let mut base_text_summary = text_cursor                                        .summary::<TextSummary>(hunk.diff_base_byte_range.end);                                    let mut needs_newline = false;                                    let mut diff_byte_range =                                        DiffOffset(hunk.diff_base_byte_range.len());                                    if base_text_summary.last_line_chars > 0 {                                        diff_byte_range.0 += 1;                                        base_text_summary.add_newline();                                        needs_newline = true;                                    } 

Lines 1–500 of 2562.