3497 lines · 101.9 KB · Rust
RawDownload
use std::ops::{Bound, Range}; use buffer_diff::{BufferDiff, BufferDiffSnapshot};use collections::HashMap;use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};use gpui::{    Action, AppContext as _, Entity, EventEmitter, Focusable, NoAction, Subscription, WeakEntity,};use language::{Buffer, Capability};use multi_buffer::{    Anchor, BufferOffset, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer,    MultiBufferPoint, MultiBufferSnapshot, PathKey,};use project::Project;use rope::Point;use text::{OffsetRangeExt as _, ToPoint as _};use ui::{    App, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Render,    Styled as _, Window, div,}; use crate::{    display_map::MultiBufferRowMapping,    split_editor_view::{SplitEditorState, SplitEditorView},};use workspace::{    ActivatePaneLeft, ActivatePaneRight, Item, ItemHandle, Pane, PaneGroup, SplitDirection,    Workspace,}; use crate::{    Autoscroll, DisplayMap, Editor, EditorEvent, ToggleCodeActions, ToggleSoftWrap,    actions::{DisableBreakpoint, EditLogBreakpoint, EnableBreakpoint, ToggleBreakpoint},    display_map::Companion,};use zed_actions::assistant::InlineAssist; pub(crate) fn convert_lhs_rows_to_rhs(    lhs_excerpt_to_rhs_excerpt: &HashMap<ExcerptId, ExcerptId>,    rhs_snapshot: &MultiBufferSnapshot,    lhs_snapshot: &MultiBufferSnapshot,    lhs_bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),) -> Vec<MultiBufferRowMapping> {    convert_rows(        lhs_excerpt_to_rhs_excerpt,        lhs_snapshot,        rhs_snapshot,        lhs_bounds,        |diff, points, buffer| {            let (points, first_group, prev_boundary) =                diff.base_text_points_to_points(points, buffer);            (points.collect(), first_group, prev_boundary)        },    )} pub(crate) fn convert_rhs_rows_to_lhs(    rhs_excerpt_to_lhs_excerpt: &HashMap<ExcerptId, ExcerptId>,    lhs_snapshot: &MultiBufferSnapshot,    rhs_snapshot: &MultiBufferSnapshot,    rhs_bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),) -> Vec<MultiBufferRowMapping> {    convert_rows(        rhs_excerpt_to_lhs_excerpt,        rhs_snapshot,        lhs_snapshot,        rhs_bounds,        |diff, points, buffer| {            let (points, first_group, prev_boundary) =                diff.points_to_base_text_points(points, buffer);            (points.collect(), first_group, prev_boundary)        },    )} fn convert_rows<F>(    excerpt_map: &HashMap<ExcerptId, ExcerptId>,    source_snapshot: &MultiBufferSnapshot,    target_snapshot: &MultiBufferSnapshot,    source_bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),    translate_fn: F,) -> Vec<MultiBufferRowMapping>where    F: Fn(        &BufferDiffSnapshot,        Vec<Point>,        &text::BufferSnapshot,    ) -> (        Vec<Range<Point>>,        Option<Range<Point>>,        Option<(Point, Range<Point>)>,    ),{    let mut result = Vec::new();     for (buffer, buffer_offset_range, source_excerpt_id) in        source_snapshot.range_to_buffer_ranges(source_bounds)    {        if let Some(translation) = convert_excerpt_rows(            excerpt_map,            source_snapshot,            target_snapshot,            source_excerpt_id,            buffer,            buffer_offset_range,            &translate_fn,        ) {            result.push(translation);        }    }     result} fn convert_excerpt_rows<F>(    excerpt_map: &HashMap<ExcerptId, ExcerptId>,    source_snapshot: &MultiBufferSnapshot,    target_snapshot: &MultiBufferSnapshot,    source_excerpt_id: ExcerptId,    source_buffer: &text::BufferSnapshot,    source_buffer_range: Range<BufferOffset>,    translate_fn: F,) -> Option<MultiBufferRowMapping>where    F: Fn(        &BufferDiffSnapshot,        Vec<Point>,        &text::BufferSnapshot,    ) -> (        Vec<Range<Point>>,        Option<Range<Point>>,        Option<(Point, Range<Point>)>,    ),{    let target_excerpt_id = excerpt_map.get(&source_excerpt_id).copied()?;    let target_buffer = target_snapshot.buffer_for_excerpt(target_excerpt_id)?;     let diff = source_snapshot.diff_for_buffer_id(source_buffer.remote_id())?;    let rhs_buffer = if source_buffer.remote_id() == diff.base_text().remote_id() {        &target_buffer    } else {        source_buffer    };     let local_start = source_buffer.offset_to_point(source_buffer_range.start.0);    let local_end = source_buffer.offset_to_point(source_buffer_range.end.0);     let mut input_points: Vec<Point> = (local_start.row..=local_end.row)        .map(|row| Point::new(row, 0))        .collect();    if local_end.column > 0 {        input_points.push(local_end);    }     let (translated_ranges, first_group, prev_boundary) =        translate_fn(&diff, input_points.clone(), rhs_buffer);     let source_multibuffer_range = source_snapshot.range_for_excerpt(source_excerpt_id)?;    let source_excerpt_start_in_multibuffer = source_multibuffer_range.start;    let source_context_range = source_snapshot.context_range_for_excerpt(source_excerpt_id)?;    let source_excerpt_start_in_buffer = source_context_range.start.to_point(&source_buffer);    let source_excerpt_end_in_buffer = source_context_range.end.to_point(&source_buffer);    let target_multibuffer_range = target_snapshot.range_for_excerpt(target_excerpt_id)?;    let target_excerpt_start_in_multibuffer = target_multibuffer_range.start;    let target_context_range = target_snapshot.context_range_for_excerpt(target_excerpt_id)?;    let target_excerpt_start_in_buffer = target_context_range.start.to_point(&target_buffer);    let target_excerpt_end_in_buffer = target_context_range.end.to_point(&target_buffer);     let boundaries: Vec<_> = input_points        .into_iter()        .zip(translated_ranges)        .map(|(source_buffer_point, target_range)| {            let source_multibuffer_point = source_excerpt_start_in_multibuffer                + (source_buffer_point - source_excerpt_start_in_buffer.min(source_buffer_point));             let clamped_target_start = target_range                .start                .max(target_excerpt_start_in_buffer)                .min(target_excerpt_end_in_buffer);            let clamped_target_end = target_range                .end                .max(target_excerpt_start_in_buffer)                .min(target_excerpt_end_in_buffer);             let target_multibuffer_start = target_excerpt_start_in_multibuffer                + (clamped_target_start - target_excerpt_start_in_buffer);             let target_multibuffer_end = target_excerpt_start_in_multibuffer                + (clamped_target_end - target_excerpt_start_in_buffer);             (                source_multibuffer_point,                target_multibuffer_start..target_multibuffer_end,            )        })        .collect();    let first_group = first_group.map(|first_group| {        let start = source_excerpt_start_in_multibuffer            + (first_group.start - source_excerpt_start_in_buffer.min(first_group.start));        let end = source_excerpt_start_in_multibuffer            + (first_group.end - source_excerpt_start_in_buffer.min(first_group.end));        start..end    });     let prev_boundary = prev_boundary.map(|(source_buffer_point, target_range)| {        let source_multibuffer_point = source_excerpt_start_in_multibuffer            + (source_buffer_point - source_excerpt_start_in_buffer.min(source_buffer_point));         let clamped_target_start = target_range            .start            .max(target_excerpt_start_in_buffer)            .min(target_excerpt_end_in_buffer);        let clamped_target_end = target_range            .end            .max(target_excerpt_start_in_buffer)            .min(target_excerpt_end_in_buffer);         let target_multibuffer_start = target_excerpt_start_in_multibuffer            + (clamped_target_start - target_excerpt_start_in_buffer);        let target_multibuffer_end = target_excerpt_start_in_multibuffer            + (clamped_target_end - target_excerpt_start_in_buffer);         (            source_multibuffer_point,            target_multibuffer_start..target_multibuffer_end,        )    });     Some(MultiBufferRowMapping {        boundaries,        first_group,        prev_boundary,        source_excerpt_end: source_excerpt_start_in_multibuffer            + (source_excerpt_end_in_buffer - source_excerpt_start_in_buffer),        target_excerpt_end: target_excerpt_start_in_multibuffer            + (target_excerpt_end_in_buffer - target_excerpt_start_in_buffer),    })} pub struct SplitDiffFeatureFlag; impl FeatureFlag for SplitDiffFeatureFlag {    const NAME: &'static str = "split-diff";     fn enabled_for_staff() -> bool {        true    }} #[derive(Clone, Copy, PartialEq, Eq, Action, Default)]#[action(namespace = editor)]struct SplitDiff; #[derive(Clone, Copy, PartialEq, Eq, Action, Default)]#[action(namespace = editor)]struct UnsplitDiff; #[derive(Clone, Copy, PartialEq, Eq, Action, Default)]#[action(namespace = editor)]pub struct ToggleSplitDiff; #[derive(Clone, Copy, PartialEq, Eq, Action, Default)]#[action(namespace = editor)]struct JumpToCorrespondingRow; /// When locked cursors mode is enabled, cursor movements in one editor will/// update the cursor position in the other editor to the corresponding row.#[derive(Clone, Copy, PartialEq, Eq, Action, Default)]#[action(namespace = editor)]pub struct ToggleLockedCursors; pub struct SplittableEditor {    rhs_multibuffer: Entity<MultiBuffer>,    rhs_editor: Entity<Editor>,    lhs: Option<LhsEditor>,    panes: PaneGroup,    workspace: WeakEntity<Workspace>,    split_state: Entity<SplitEditorState>,    locked_cursors: bool,    _subscriptions: Vec<Subscription>,} struct LhsEditor {    multibuffer: Entity<MultiBuffer>,    editor: Entity<Editor>,    pane: Entity<Pane>,    has_latest_selection: bool,    _subscriptions: Vec<Subscription>,} impl SplittableEditor {    pub fn rhs_editor(&self) -> &Entity<Editor> {        &self.rhs_editor    }     pub fn lhs_editor(&self) -> Option<&Entity<Editor>> {        self.lhs.as_ref().map(|s| &s.editor)    }     pub fn is_split(&self) -> bool {        self.lhs.is_some()    }     pub fn last_selected_editor(&self) -> &Entity<Editor> {        if let Some(lhs) = &self.lhs            && lhs.has_latest_selection        {            &lhs.editor        } else {            &self.rhs_editor        }    }     pub fn new_unsplit(        rhs_multibuffer: Entity<MultiBuffer>,        project: Entity<Project>,        workspace: Entity<Workspace>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Self {        let rhs_editor = cx.new(|cx| {            let mut editor =                Editor::for_multibuffer(rhs_multibuffer.clone(), Some(project.clone()), window, cx);            editor.set_expand_all_diff_hunks(cx);            editor        });        let pane = cx.new(|cx| {            let mut pane = Pane::new(                workspace.downgrade(),                project,                Default::default(),                None,                NoAction.boxed_clone(),                true,                window,                cx,            );            pane.set_should_display_tab_bar(|_, _| false);            pane.add_item(rhs_editor.boxed_clone(), true, true, None, window, cx);            pane        });        let panes = PaneGroup::new(pane);        // TODO(split-diff) we might want to tag editor events with whether they came from rhs/lhs        let subscriptions =            vec![cx.subscribe(                &rhs_editor,                |this, _, event: &EditorEvent, cx| match event {                    EditorEvent::ExpandExcerptsRequested {                        excerpt_ids,                        lines,                        direction,                    } => {                        this.expand_excerpts(excerpt_ids.iter().copied(), *lines, *direction, cx);                    }                    EditorEvent::SelectionsChanged { .. } => {                        if let Some(lhs) = &mut this.lhs {                            lhs.has_latest_selection = false;                        }                        cx.emit(event.clone());                    }                    _ => cx.emit(event.clone()),                },            )];         window.defer(cx, {            let workspace = workspace.downgrade();            let rhs_editor = rhs_editor.downgrade();            move |window, cx| {                workspace                    .update(cx, |workspace, cx| {                        rhs_editor.update(cx, |editor, cx| {                            editor.added_to_workspace(workspace, window, cx);                        })                    })                    .ok();            }        });        let split_state = cx.new(|cx| SplitEditorState::new(cx));        Self {            rhs_editor,            rhs_multibuffer,            lhs: None,            panes,            workspace: workspace.downgrade(),            split_state,            locked_cursors: false,            _subscriptions: subscriptions,        }    }     fn split(&mut self, _: &SplitDiff, window: &mut Window, cx: &mut Context<Self>) {        if !cx.has_flag::<SplitDiffFeatureFlag>() {            return;        }        if self.lhs.is_some() {            return;        }        let Some(workspace) = self.workspace.upgrade() else {            return;        };        let project = workspace.read(cx).project().clone();         let lhs_multibuffer = cx.new(|cx| {            let mut multibuffer = MultiBuffer::new(Capability::ReadOnly);            multibuffer.set_all_diff_hunks_expanded(cx);            multibuffer        });        let lhs_editor = cx.new(|cx| {            let mut editor =                Editor::for_multibuffer(lhs_multibuffer.clone(), Some(project.clone()), window, cx);            editor.number_deleted_lines = true;            editor.set_delegate_expand_excerpts(true);            editor        });        let lhs_pane = cx.new(|cx| {            let mut pane = Pane::new(                workspace.downgrade(),                workspace.read(cx).project().clone(),                Default::default(),                None,                NoAction.boxed_clone(),                true,                window,                cx,            );            pane.set_should_display_tab_bar(|_, _| false);            pane.add_item(                ItemHandle::boxed_clone(&lhs_editor),                false,                false,                None,                window,                cx,            );            pane        });         let subscriptions =            vec![cx.subscribe(                &lhs_editor,                |this, _, event: &EditorEvent, cx| match event {                    EditorEvent::ExpandExcerptsRequested {                        excerpt_ids,                        lines,                        direction,                    } => {                        if this.lhs.is_some() {                            let rhs_display_map = this.rhs_editor.read(cx).display_map.read(cx);                            let rhs_ids: Vec<_> = excerpt_ids                                .iter()                                .filter_map(|id| {                                    rhs_display_map.companion_excerpt_to_my_excerpt(*id, cx)                                })                                .collect();                            this.expand_excerpts(rhs_ids.into_iter(), *lines, *direction, cx);                        }                    }                    EditorEvent::SelectionsChanged { .. } => {                        if let Some(lhs) = &mut this.lhs {                            lhs.has_latest_selection = true;                        }                        cx.emit(event.clone());                    }                    _ => cx.emit(event.clone()),                },            )];        let mut lhs = LhsEditor {            editor: lhs_editor,            multibuffer: lhs_multibuffer,            pane: lhs_pane.clone(),            has_latest_selection: false,            _subscriptions: subscriptions,        };        let rhs_display_map = self.rhs_editor.read(cx).display_map.clone();        let lhs_display_map = lhs.editor.read(cx).display_map.clone();        let rhs_display_map_id = rhs_display_map.entity_id();         self.rhs_editor.update(cx, |editor, cx| {            editor.set_delegate_expand_excerpts(true);            editor.buffer().update(cx, |rhs_multibuffer, cx| {                rhs_multibuffer.set_show_deleted_hunks(false, cx);                rhs_multibuffer.set_use_extended_diff_range(true, cx);            })        });         let path_diffs: Vec<_> = {            let rhs_multibuffer = self.rhs_multibuffer.read(cx);            rhs_multibuffer                .paths()                .filter_map(|path| {                    let excerpt_id = rhs_multibuffer.excerpts_for_path(path).next()?;                    let snapshot = rhs_multibuffer.snapshot(cx);                    let buffer = snapshot.buffer_for_excerpt(excerpt_id)?;                    let diff = rhs_multibuffer.diff_for(buffer.remote_id())?;                    Some((path.clone(), diff))                })                .collect()        };         let rhs_folded_buffers = rhs_display_map.read(cx).folded_buffers().clone();

Lines 1–500 of 3497.