3528 lines · 104.2 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),    })} 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 {    primary_multibuffer: Entity<MultiBuffer>,    primary_editor: Entity<Editor>,    secondary: Option<SecondaryEditor>,    panes: PaneGroup,    workspace: WeakEntity<Workspace>,    split_state: Entity<SplitEditorState>,    locked_cursors: bool,    _subscriptions: Vec<Subscription>,} struct SecondaryEditor {    multibuffer: Entity<MultiBuffer>,    editor: Entity<Editor>,    pane: Entity<Pane>,    has_latest_selection: bool,    _subscriptions: Vec<Subscription>,} impl SplittableEditor {    pub fn primary_editor(&self) -> &Entity<Editor> {        &self.primary_editor    }     pub fn secondary_editor(&self) -> Option<&Entity<Editor>> {        self.secondary.as_ref().map(|s| &s.editor)    }     pub fn is_split(&self) -> bool {        self.secondary.is_some()    }     pub fn last_selected_editor(&self) -> &Entity<Editor> {        if let Some(secondary) = &self.secondary            && secondary.has_latest_selection        {            &secondary.editor        } else {            &self.primary_editor        }    }     pub fn new_unsplit(        primary_multibuffer: Entity<MultiBuffer>,        project: Entity<Project>,        workspace: Entity<Workspace>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Self {        let primary_editor = cx.new(|cx| {            let mut editor = Editor::for_multibuffer(                primary_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(primary_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 primary/secondary        let subscriptions = vec![cx.subscribe(            &primary_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(secondary) = &mut this.secondary {                        secondary.has_latest_selection = false;                    }                    cx.emit(event.clone());                }                _ => cx.emit(event.clone()),            },        )];         window.defer(cx, {            let workspace = workspace.downgrade();            let primary_editor = primary_editor.downgrade();            move |window, cx| {                workspace                    .update(cx, |workspace, cx| {                        primary_editor.update(cx, |editor, cx| {                            editor.added_to_workspace(workspace, window, cx);                        })                    })                    .ok();            }        });        let split_state = cx.new(|cx| SplitEditorState::new(cx));        Self {            primary_editor,            primary_multibuffer,            secondary: 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.secondary.is_some() {            return;        }        let Some(workspace) = self.workspace.upgrade() else {            return;        };        let project = workspace.read(cx).project().clone();         let secondary_multibuffer = cx.new(|cx| {            let mut multibuffer = MultiBuffer::new(Capability::ReadOnly);            multibuffer.set_all_diff_hunks_expanded(cx);            multibuffer        });        let secondary_editor = cx.new(|cx| {            let mut editor = Editor::for_multibuffer(                secondary_multibuffer.clone(),                Some(project.clone()),                window,                cx,            );            editor.number_deleted_lines = true;            editor.set_delegate_expand_excerpts(true);            editor        });        let secondary_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(&secondary_editor),                false,                false,                None,                window,                cx,            );            pane        });         let subscriptions = vec![cx.subscribe(            &secondary_editor,            |this, _, event: &EditorEvent, cx| match event {                EditorEvent::ExpandExcerptsRequested {                    excerpt_ids,                    lines,                    direction,                } => {                    if this.secondary.is_some() {                        let primary_display_map = this.primary_editor.read(cx).display_map.read(cx);                        let primary_ids: Vec<_> = excerpt_ids                            .iter()                            .filter_map(|id| {                                primary_display_map.companion_excerpt_to_my_excerpt(*id, cx)                            })                            .collect();                        this.expand_excerpts(primary_ids.into_iter(), *lines, *direction, cx);                    }                }                EditorEvent::SelectionsChanged { .. } => {                    if let Some(secondary) = &mut this.secondary {                        secondary.has_latest_selection = true;                    }                    cx.emit(event.clone());                }                _ => cx.emit(event.clone()),            },        )];        let mut secondary = SecondaryEditor {            editor: secondary_editor,            multibuffer: secondary_multibuffer,            pane: secondary_pane.clone(),            has_latest_selection: false,            _subscriptions: subscriptions,        };        let primary_display_map = self.primary_editor.read(cx).display_map.clone();        let secondary_display_map = secondary.editor.read(cx).display_map.clone();        let rhs_display_map_id = primary_display_map.entity_id();         self.primary_editor.update(cx, |editor, cx| {            editor.set_delegate_expand_excerpts(true);            editor.buffer().update(cx, |primary_multibuffer, cx| {                primary_multibuffer.set_show_deleted_hunks(false, cx);                primary_multibuffer.set_use_extended_diff_range(true, cx);            })        });         let path_diffs: Vec<_> = {            let primary_multibuffer = self.primary_multibuffer.read(cx);            primary_multibuffer                .paths()                .filter_map(|path| {                    let excerpt_id = primary_multibuffer.excerpts_for_path(path).next()?;                    let snapshot = primary_multibuffer.snapshot(cx);                    let buffer = snapshot.buffer_for_excerpt(excerpt_id)?;                    let diff = primary_multibuffer.diff_for(buffer.remote_id())?;

Lines 1–500 of 3528.