1498 lines · 63.3 KB · Rust
RawDownload
use collections::{HashMap, HashSet};use git::diff::DiffHunkStatus;use gpui::{    Action, AnchorCorner, AppContext, CursorStyle, Hsla, Model, MouseButton, Subscription, Task,    View,};use language::{Buffer, BufferId, Point};use multi_buffer::{    Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,    MultiBufferSnapshot, ToOffset, ToPoint,};use project::buffer_store::BufferChangeSet;use std::{ops::Range, sync::Arc};use sum_tree::TreeMap;use text::OffsetRangeExt;use ui::{    prelude::*, ActiveTheme, ContextMenu, IconButtonShape, InteractiveElement, IntoElement,    ParentElement, PopoverMenu, Styled, Tooltip, ViewContext, VisualContext,};use util::RangeExt;use workspace::Item; use crate::{    editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyAllDiffHunks,    ApplyDiffHunk, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight,    DisplayRow, DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk,    RevertFile, RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,}; #[derive(Debug, Clone)]pub(super) struct HoveredHunk {    pub multi_buffer_range: Range<Anchor>,    pub status: DiffHunkStatus,    pub diff_base_byte_range: Range<usize>,} #[derive(Default)]pub(super) struct DiffMap {    pub(crate) hunks: Vec<ExpandedHunk>,    pub(crate) diff_bases: HashMap<BufferId, DiffBaseState>,    pub(crate) snapshot: DiffMapSnapshot,    hunk_update_tasks: HashMap<Option<BufferId>, Task<()>>,    expand_all: bool,} #[derive(Debug, Clone)]pub(super) struct ExpandedHunk {    pub blocks: Vec<CustomBlockId>,    pub hunk_range: Range<Anchor>,    pub diff_base_byte_range: Range<usize>,    pub status: DiffHunkStatus,    pub folded: bool,} #[derive(Clone, Debug, Default)]pub(crate) struct DiffMapSnapshot(TreeMap<BufferId, git::diff::BufferDiff>); pub(crate) struct DiffBaseState {    pub(crate) change_set: Model<BufferChangeSet>,    pub(crate) last_version: Option<usize>,    _subscription: Subscription,} #[derive(Debug, Clone, PartialEq, Eq)]pub enum DisplayDiffHunk {    Folded {        display_row: DisplayRow,    },     Unfolded {        diff_base_byte_range: Range<usize>,        display_row_range: Range<DisplayRow>,        multi_buffer_range: Range<Anchor>,        status: DiffHunkStatus,    },} impl DiffMap {    pub fn snapshot(&self) -> DiffMapSnapshot {        self.snapshot.clone()    }     pub fn add_change_set(        &mut self,        change_set: Model<BufferChangeSet>,        cx: &mut ViewContext<Editor>,    ) {        let buffer_id = change_set.read(cx).buffer_id;        self.snapshot            .0            .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone());        self.diff_bases.insert(            buffer_id,            DiffBaseState {                last_version: None,                _subscription: cx.observe(&change_set, move |editor, change_set, cx| {                    editor                        .diff_map                        .snapshot                        .0                        .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone());                    Editor::sync_expanded_diff_hunks(&mut editor.diff_map, buffer_id, cx);                }),                change_set,            },        );        Editor::sync_expanded_diff_hunks(self, buffer_id, cx);    }     pub fn hunks(&self, include_folded: bool) -> impl Iterator<Item = &ExpandedHunk> {        self.hunks            .iter()            .filter(move |hunk| include_folded || !hunk.folded)    }} impl DiffMapSnapshot {    pub fn is_empty(&self) -> bool {        self.0.values().all(|diff| diff.is_empty())    }     pub fn diff_hunks<'a>(        &'a self,        buffer_snapshot: &'a MultiBufferSnapshot,    ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a {        self.diff_hunks_in_range(0..buffer_snapshot.len(), buffer_snapshot)    }     pub fn diff_hunks_in_range<'a, T: ToOffset>(        &'a self,        range: Range<T>,        buffer_snapshot: &'a MultiBufferSnapshot,    ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a {        let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot);        buffer_snapshot            .excerpts_for_range(range.clone())            .filter_map(move |excerpt| {                let buffer = excerpt.buffer();                let buffer_id = buffer.remote_id();                let diff = self.0.get(&buffer_id)?;                let buffer_range = excerpt.map_range_to_buffer(range.clone());                let buffer_range =                    buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end);                Some(                    diff.hunks_intersecting_range(buffer_range, excerpt.buffer())                        .map(move |hunk| {                            let start =                                excerpt.map_point_from_buffer(Point::new(hunk.row_range.start, 0));                            let end =                                excerpt.map_point_from_buffer(Point::new(hunk.row_range.end, 0));                            MultiBufferDiffHunk {                                row_range: MultiBufferRow(start.row)..MultiBufferRow(end.row),                                buffer_id,                                buffer_range: hunk.buffer_range.clone(),                                diff_base_byte_range: hunk.diff_base_byte_range.clone(),                            }                        }),                )            })            .flatten()    }     pub fn diff_hunks_in_range_rev<'a, T: ToOffset>(        &'a self,        range: Range<T>,        buffer_snapshot: &'a MultiBufferSnapshot,    ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a {        let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot);        buffer_snapshot            .excerpts_for_range_rev(range.clone())            .filter_map(move |excerpt| {                let buffer = excerpt.buffer();                let buffer_id = buffer.remote_id();                let diff = self.0.get(&buffer_id)?;                let buffer_range = excerpt.map_range_to_buffer(range.clone());                let buffer_range =                    buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end);                Some(                    diff.hunks_intersecting_range_rev(buffer_range, excerpt.buffer())                        .map(move |hunk| {                            let start_row = excerpt                                .map_point_from_buffer(Point::new(hunk.row_range.start, 0))                                .row;                            let end_row = excerpt                                .map_point_from_buffer(Point::new(hunk.row_range.end, 0))                                .row;                            MultiBufferDiffHunk {                                row_range: MultiBufferRow(start_row)..MultiBufferRow(end_row),                                buffer_id,                                buffer_range: hunk.buffer_range.clone(),                                diff_base_byte_range: hunk.diff_base_byte_range.clone(),                            }                        }),                )            })            .flatten()    }} impl Editor {    pub fn set_expand_all_diff_hunks(&mut self) {        self.diff_map.expand_all = true;    }     pub(super) fn toggle_hovered_hunk(        &mut self,        hovered_hunk: &HoveredHunk,        cx: &mut ViewContext<Editor>,    ) {        let editor_snapshot = self.snapshot(cx);        if let Some(diff_hunk) = to_diff_hunk(hovered_hunk, &editor_snapshot.buffer_snapshot) {            self.toggle_hunks_expanded(vec![diff_hunk], cx);            self.change_selections(None, cx, |selections| selections.refresh());        }    }     pub fn toggle_hunk_diff(&mut self, _: &ToggleHunkDiff, cx: &mut ViewContext<Self>) {        let snapshot = self.snapshot(cx);        let selections = self.selections.all(cx);        self.toggle_hunks_expanded(hunks_for_selections(&snapshot, &selections), cx);    }     pub fn expand_all_hunk_diffs(&mut self, _: &ExpandAllHunkDiffs, cx: &mut ViewContext<Self>) {        let snapshot = self.snapshot(cx);        let display_rows_with_expanded_hunks = self            .diff_map            .hunks(false)            .map(|hunk| &hunk.hunk_range)            .map(|anchor_range| {                (                    anchor_range                        .start                        .to_display_point(&snapshot.display_snapshot)                        .row(),                    anchor_range                        .end                        .to_display_point(&snapshot.display_snapshot)                        .row(),                )            })            .collect::<HashMap<_, _>>();        let hunks = self            .diff_map            .snapshot            .diff_hunks(&snapshot.display_snapshot.buffer_snapshot)            .filter(|hunk| {                let hunk_display_row_range = Point::new(hunk.row_range.start.0, 0)                    .to_display_point(&snapshot.display_snapshot)                    ..Point::new(hunk.row_range.end.0, 0)                        .to_display_point(&snapshot.display_snapshot);                let row_range_end =                    display_rows_with_expanded_hunks.get(&hunk_display_row_range.start.row());                row_range_end.is_none() || row_range_end != Some(&hunk_display_row_range.end.row())            });        self.toggle_hunks_expanded(hunks.collect(), cx);    }     fn toggle_hunks_expanded(        &mut self,        hunks_to_toggle: Vec<MultiBufferDiffHunk>,        cx: &mut ViewContext<Self>,    ) {        if self.diff_map.expand_all {            return;        }         let previous_toggle_task = self.diff_map.hunk_update_tasks.remove(&None);        let new_toggle_task = cx.spawn(move |editor, mut cx| async move {            if let Some(task) = previous_toggle_task {                task.await;            }             editor                .update(&mut cx, |editor, cx| {                    let snapshot = editor.snapshot(cx);                    let mut hunks_to_toggle = hunks_to_toggle.into_iter().fuse().peekable();                    let mut highlights_to_remove = Vec::with_capacity(editor.diff_map.hunks.len());                    let mut blocks_to_remove = HashSet::default();                    let mut hunks_to_expand = Vec::new();                    editor.diff_map.hunks.retain(|expanded_hunk| {                        if expanded_hunk.folded {                            return true;                        }                        let expanded_hunk_row_range = expanded_hunk                            .hunk_range                            .start                            .to_display_point(&snapshot)                            .row()                            ..expanded_hunk                                .hunk_range                                .end                                .to_display_point(&snapshot)                                .row();                        let mut retain = true;                        while let Some(hunk_to_toggle) = hunks_to_toggle.peek() {                            match diff_hunk_to_display(hunk_to_toggle, &snapshot) {                                DisplayDiffHunk::Folded { .. } => {                                    hunks_to_toggle.next();                                    continue;                                }                                DisplayDiffHunk::Unfolded {                                    diff_base_byte_range,                                    display_row_range,                                    multi_buffer_range,                                    status,                                } => {                                    let hunk_to_toggle_row_range = display_row_range;                                    if hunk_to_toggle_row_range.start > expanded_hunk_row_range.end                                    {                                        break;                                    } else if expanded_hunk_row_range == hunk_to_toggle_row_range {                                        highlights_to_remove.push(expanded_hunk.hunk_range.clone());                                        blocks_to_remove                                            .extend(expanded_hunk.blocks.iter().copied());                                        hunks_to_toggle.next();                                        retain = false;                                        break;                                    } else {                                        hunks_to_expand.push(HoveredHunk {                                            status,                                            multi_buffer_range,                                            diff_base_byte_range,                                        });                                        hunks_to_toggle.next();                                        continue;                                    }                                }                            }                        }                         retain                    });                    for hunk in hunks_to_toggle {                        let remaining_hunk_point_range = Point::new(hunk.row_range.start.0, 0)                            ..Point::new(hunk.row_range.end.0, 0);                        let hunk_start = snapshot                            .buffer_snapshot                            .anchor_before(remaining_hunk_point_range.start);                        let hunk_end = snapshot                            .buffer_snapshot                            .anchor_in_excerpt(hunk_start.excerpt_id, hunk.buffer_range.end)                            .unwrap();                        hunks_to_expand.push(HoveredHunk {                            status: hunk_status(&hunk),                            multi_buffer_range: hunk_start..hunk_end,                            diff_base_byte_range: hunk.diff_base_byte_range.clone(),                        });                    }                     editor.remove_highlighted_rows::<DiffRowHighlight>(highlights_to_remove, cx);                    editor.remove_blocks(blocks_to_remove, None, cx);                    for hunk in hunks_to_expand {                        editor.expand_diff_hunk(None, &hunk, cx);                    }                    cx.notify();                })                .ok();        });         self.diff_map            .hunk_update_tasks            .insert(None, cx.background_executor().spawn(new_toggle_task));    }     pub(super) fn expand_diff_hunk(        &mut self,        diff_base_buffer: Option<Model<Buffer>>,        hunk: &HoveredHunk,        cx: &mut ViewContext<'_, Editor>,    ) -> Option<()> {        let buffer = self.buffer.clone();        let multi_buffer_snapshot = buffer.read(cx).snapshot(cx);        let hunk_range = hunk.multi_buffer_range.clone();        let buffer_id = hunk_range.start.buffer_id?;        let diff_base_buffer = diff_base_buffer.or_else(|| {            self.diff_map                .diff_bases                .get(&buffer_id)?                .change_set                .read(cx)                .base_text                .clone()        })?;         let diff_base = diff_base_buffer.read(cx);        let diff_start_row = diff_base            .offset_to_point(hunk.diff_base_byte_range.start)            .row;        let diff_end_row = diff_base.offset_to_point(hunk.diff_base_byte_range.end).row;        let deleted_text_lines = diff_end_row - diff_start_row;         let block_insert_index = self            .diff_map            .hunks            .binary_search_by(|probe| {                probe                    .hunk_range                    .start                    .cmp(&hunk_range.start, &multi_buffer_snapshot)            })            .err()?;         let blocks;        match hunk.status {            DiffHunkStatus::Removed => {                blocks = self.insert_blocks(                    [                        self.hunk_header_block(&hunk, cx),                        Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx),                    ],                    None,                    cx,                );            }            DiffHunkStatus::Added => {                self.highlight_rows::<DiffRowHighlight>(                    hunk_range.clone(),                    added_hunk_color(cx),                    false,                    cx,                );                blocks = self.insert_blocks([self.hunk_header_block(&hunk, cx)], None, cx);            }            DiffHunkStatus::Modified => {                self.highlight_rows::<DiffRowHighlight>(                    hunk_range.clone(),                    added_hunk_color(cx),                    false,                    cx,                );                blocks = self.insert_blocks(                    [                        self.hunk_header_block(&hunk, cx),                        Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx),                    ],                    None,                    cx,                );            }        };        self.diff_map.hunks.insert(            block_insert_index,            ExpandedHunk {                blocks,                hunk_range,                status: hunk.status,                folded: false,                diff_base_byte_range: hunk.diff_base_byte_range.clone(),            },        );         Some(())    }     fn apply_diff_hunks_in_range(        &mut self,        range: Range<Anchor>,        cx: &mut ViewContext<'_, Editor>,    ) -> Option<()> {        let (buffer, range, _) = self            .buffer            .read(cx)            .range_to_buffer_ranges(range, cx)            .into_iter()            .next()?;         buffer.update(cx, |branch_buffer, cx| {            branch_buffer.merge_into_base(vec![range], cx);        });         if let Some(project) = self.project.clone() {            self.save(true, project, cx).detach_and_log_err(cx);        }         None    }     pub(crate) fn apply_all_diff_hunks(        &mut self,        _: &ApplyAllDiffHunks,        cx: &mut ViewContext<Self>,    ) {        let buffers = self.buffer.read(cx).all_buffers();        for branch_buffer in buffers {            branch_buffer.update(cx, |branch_buffer, cx| {                branch_buffer.merge_into_base(Vec::new(), cx);            });        }         if let Some(project) = self.project.clone() {            self.save(true, project, cx).detach_and_log_err(cx);        }    }     pub(crate) fn apply_selected_diff_hunks(        &mut self,        _: &ApplyDiffHunk,        cx: &mut ViewContext<Self>,    ) {        let snapshot = self.snapshot(cx);

Lines 1–500 of 1498.