1719 lines · 60.0 KB · Rust
RawDownload
use crate::{    editor_settings::SeedQuerySetting,    persistence::{SerializedEditor, DB},    scroll::ScrollAnchor,    Anchor, Autoscroll, Editor, EditorEvent, EditorSettings, ExcerptId, ExcerptRange, MultiBuffer,    MultiBufferSnapshot, NavigationData, SearchWithinRange, ToPoint as _,};use anyhow::{anyhow, Context as _, Result};use collections::HashSet;use file_icons::FileIcons;use futures::future::try_join_all;use git::repository::GitFileStatus;use gpui::{    point, AnyElement, AppContext, AsyncWindowContext, Context, Entity, EntityId, EventEmitter,    IntoElement, Model, ParentElement, Pixels, SharedString, Styled, Task, View, ViewContext,    VisualContext, WeakView, WindowContext,};use language::{    proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, Point, SelectionGoal,};use multi_buffer::AnchorRangeExt;use project::{    project_settings::ProjectSettings, search::SearchQuery, FormatTrigger, Item as _, Project,    ProjectPath,};use rpc::proto::{self, update_view, PeerId};use settings::Settings;use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams}; use std::{    any::TypeId,    borrow::Cow,    cmp::{self, Ordering},    iter,    ops::Range,    path::Path,    sync::Arc,};use text::{BufferId, Selection};use theme::{Theme, ThemeSettings};use ui::{h_flex, prelude::*, Label};use util::{paths::PathExt, ResultExt, TryFutureExt};use workspace::item::{BreadcrumbText, FollowEvent};use workspace::{    item::{FollowableItem, Item, ItemEvent, ProjectItem},    searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},    ItemId, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,}; pub const MAX_TAB_TITLE_LEN: usize = 24; impl FollowableItem for Editor {    fn remote_id(&self) -> Option<ViewId> {        self.remote_id    }     fn from_state_proto(        workspace: View<Workspace>,        remote_id: ViewId,        state: &mut Option<proto::view::Variant>,        cx: &mut WindowContext,    ) -> Option<Task<Result<View<Self>>>> {        let project = workspace.read(cx).project().to_owned();        let Some(proto::view::Variant::Editor(_)) = state else {            return None;        };        let Some(proto::view::Variant::Editor(state)) = state.take() else {            unreachable!()        };         let replica_id = project.read(cx).replica_id();        let buffer_ids = state            .excerpts            .iter()            .map(|excerpt| excerpt.buffer_id)            .collect::<HashSet<_>>();        let buffers = project.update(cx, |project, cx| {            buffer_ids                .iter()                .map(|id| BufferId::new(*id).map(|id| project.open_buffer_by_id(id, cx)))                .collect::<Result<Vec<_>>>()        });         Some(cx.spawn(|mut cx| async move {            let mut buffers = futures::future::try_join_all(buffers?)                .await                .debug_assert_ok("leaders don't share views for unshared buffers")?;             let editor = cx.update(|cx| {                let multibuffer = cx.new_model(|cx| {                    let mut multibuffer;                    if state.singleton && buffers.len() == 1 {                        multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)                    } else {                        multibuffer = MultiBuffer::new(replica_id, project.read(cx).capability());                        let mut excerpts = state.excerpts.into_iter().peekable();                        while let Some(excerpt) = excerpts.peek() {                            let Ok(buffer_id) = BufferId::new(excerpt.buffer_id) else {                                continue;                            };                            let buffer_excerpts = iter::from_fn(|| {                                let excerpt = excerpts.peek()?;                                (excerpt.buffer_id == u64::from(buffer_id))                                    .then(|| excerpts.next().unwrap())                            });                            let buffer =                                buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);                            if let Some(buffer) = buffer {                                multibuffer.push_excerpts(                                    buffer.clone(),                                    buffer_excerpts.filter_map(deserialize_excerpt_range),                                    cx,                                );                            }                        }                    };                     if let Some(title) = &state.title {                        multibuffer = multibuffer.with_title(title.clone())                    }                     multibuffer                });                 cx.new_view(|cx| {                    let mut editor =                        Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx);                    editor.remote_id = Some(remote_id);                    editor                })            })?;             update_editor_from_message(                editor.downgrade(),                project,                proto::update_view::Editor {                    selections: state.selections,                    pending_selection: state.pending_selection,                    scroll_top_anchor: state.scroll_top_anchor,                    scroll_x: state.scroll_x,                    scroll_y: state.scroll_y,                    ..Default::default()                },                &mut cx,            )            .await?;             Ok(editor)        }))    }     fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {        self.leader_peer_id = leader_peer_id;        if self.leader_peer_id.is_some() {            self.buffer.update(cx, |buffer, cx| {                buffer.remove_active_selections(cx);            });        } else if self.focus_handle.is_focused(cx) {            self.buffer.update(cx, |buffer, cx| {                buffer.set_active_selections(                    &self.selections.disjoint_anchors(),                    self.selections.line_mode,                    self.cursor_shape,                    cx,                );            });        }        cx.notify();    }     fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {        let buffer = self.buffer.read(cx);        if buffer            .as_singleton()            .and_then(|buffer| buffer.read(cx).file())            .map_or(false, |file| file.is_private())        {            return None;        }         let scroll_anchor = self.scroll_manager.anchor();        let excerpts = buffer            .read(cx)            .excerpts()            .map(|(id, buffer, range)| proto::Excerpt {                id: id.to_proto(),                buffer_id: buffer.remote_id().into(),                context_start: Some(serialize_text_anchor(&range.context.start)),                context_end: Some(serialize_text_anchor(&range.context.end)),                primary_start: range                    .primary                    .as_ref()                    .map(|range| serialize_text_anchor(&range.start)),                primary_end: range                    .primary                    .as_ref()                    .map(|range| serialize_text_anchor(&range.end)),            })            .collect();         Some(proto::view::Variant::Editor(proto::view::Editor {            singleton: buffer.is_singleton(),            title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),            excerpts,            scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),            scroll_x: scroll_anchor.offset.x,            scroll_y: scroll_anchor.offset.y,            selections: self                .selections                .disjoint_anchors()                .iter()                .map(serialize_selection)                .collect(),            pending_selection: self                .selections                .pending_anchor()                .as_ref()                .map(serialize_selection),        }))    }     fn to_follow_event(event: &EditorEvent) -> Option<workspace::item::FollowEvent> {        match event {            EditorEvent::Edited { .. } => Some(FollowEvent::Unfollow),            EditorEvent::SelectionsChanged { local }            | EditorEvent::ScrollPositionChanged { local, .. } => {                if *local {                    Some(FollowEvent::Unfollow)                } else {                    None                }            }            _ => None,        }    }     fn add_event_to_update_proto(        &self,        event: &EditorEvent,        update: &mut Option<proto::update_view::Variant>,        cx: &WindowContext,    ) -> bool {        let update =            update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));         match update {            proto::update_view::Variant::Editor(update) => match event {                EditorEvent::ExcerptsAdded {                    buffer,                    predecessor,                    excerpts,                } => {                    let buffer_id = buffer.read(cx).remote_id();                    let mut excerpts = excerpts.iter();                    if let Some((id, range)) = excerpts.next() {                        update.inserted_excerpts.push(proto::ExcerptInsertion {                            previous_excerpt_id: Some(predecessor.to_proto()),                            excerpt: serialize_excerpt(buffer_id, id, range),                        });                        update.inserted_excerpts.extend(excerpts.map(|(id, range)| {                            proto::ExcerptInsertion {                                previous_excerpt_id: None,                                excerpt: serialize_excerpt(buffer_id, id, range),                            }                        }))                    }                    true                }                EditorEvent::ExcerptsRemoved { ids } => {                    update                        .deleted_excerpts                        .extend(ids.iter().map(ExcerptId::to_proto));                    true                }                EditorEvent::ScrollPositionChanged { autoscroll, .. } if !autoscroll => {                    let scroll_anchor = self.scroll_manager.anchor();                    update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));                    update.scroll_x = scroll_anchor.offset.x;                    update.scroll_y = scroll_anchor.offset.y;                    true                }                EditorEvent::SelectionsChanged { .. } => {                    update.selections = self                        .selections                        .disjoint_anchors()                        .iter()                        .map(serialize_selection)                        .collect();                    update.pending_selection = self                        .selections                        .pending_anchor()                        .as_ref()                        .map(serialize_selection);                    true                }                _ => false,            },        }    }     fn apply_update_proto(        &mut self,        project: &Model<Project>,        message: update_view::Variant,        cx: &mut ViewContext<Self>,    ) -> Task<Result<()>> {        let update_view::Variant::Editor(message) = message;        let project = project.clone();        cx.spawn(|this, mut cx| async move {            update_editor_from_message(this, project, message, &mut cx).await        })    }     fn is_project_item(&self, _cx: &WindowContext) -> bool {        true    }     fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup> {        let self_singleton = self.buffer.read(cx).as_singleton()?;        let other_singleton = existing.buffer.read(cx).as_singleton()?;        if self_singleton == other_singleton {            Some(Dedup::KeepExisting)        } else {            None        }    }} async fn update_editor_from_message(    this: WeakView<Editor>,    project: Model<Project>,    message: proto::update_view::Editor,    cx: &mut AsyncWindowContext,) -> Result<()> {    // Open all of the buffers of which excerpts were added to the editor.    let inserted_excerpt_buffer_ids = message        .inserted_excerpts        .iter()        .filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id))        .collect::<HashSet<_>>();    let inserted_excerpt_buffers = project.update(cx, |project, cx| {        inserted_excerpt_buffer_ids            .into_iter()            .map(|id| BufferId::new(id).map(|id| project.open_buffer_by_id(id, cx)))            .collect::<Result<Vec<_>>>()    })??;    let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;     // Update the editor's excerpts.    this.update(cx, |editor, cx| {        editor.buffer.update(cx, |multibuffer, cx| {            let mut removed_excerpt_ids = message                .deleted_excerpts                .into_iter()                .map(ExcerptId::from_proto)                .collect::<Vec<_>>();            removed_excerpt_ids.sort_by({                let multibuffer = multibuffer.read(cx);                move |a, b| a.cmp(&b, &multibuffer)            });             let mut insertions = message.inserted_excerpts.into_iter().peekable();            while let Some(insertion) = insertions.next() {                let Some(excerpt) = insertion.excerpt else {                    continue;                };                let Some(previous_excerpt_id) = insertion.previous_excerpt_id else {                    continue;                };                let buffer_id = BufferId::new(excerpt.buffer_id)?;                let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {                    continue;                };                 let adjacent_excerpts = iter::from_fn(|| {                    let insertion = insertions.peek()?;                    if insertion.previous_excerpt_id.is_none()                        && insertion.excerpt.as_ref()?.buffer_id == u64::from(buffer_id)                    {                        insertions.next()?.excerpt                    } else {                        None                    }                });                 multibuffer.insert_excerpts_with_ids_after(                    ExcerptId::from_proto(previous_excerpt_id),                    buffer,                    [excerpt]                        .into_iter()                        .chain(adjacent_excerpts)                        .filter_map(|excerpt| {                            Some((                                ExcerptId::from_proto(excerpt.id),                                deserialize_excerpt_range(excerpt)?,                            ))                        }),                    cx,                );            }             multibuffer.remove_excerpts(removed_excerpt_ids, cx);            Result::<(), anyhow::Error>::Ok(())        })    })??;     // Deserialize the editor state.    let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {        let buffer = editor.buffer.read(cx).read(cx);        let selections = message            .selections            .into_iter()            .filter_map(|selection| deserialize_selection(&buffer, selection))            .collect::<Vec<_>>();        let pending_selection = message            .pending_selection            .and_then(|selection| deserialize_selection(&buffer, selection));        let scroll_top_anchor = message            .scroll_top_anchor            .and_then(|anchor| deserialize_anchor(&buffer, anchor));        anyhow::Ok((selections, pending_selection, scroll_top_anchor))    })??;     // Wait until the buffer has received all of the operations referenced by    // the editor's new state.    this.update(cx, |editor, cx| {        editor.buffer.update(cx, |buffer, cx| {            buffer.wait_for_anchors(                selections                    .iter()                    .chain(pending_selection.as_ref())                    .flat_map(|selection| [selection.start, selection.end])                    .chain(scroll_top_anchor),                cx,            )        })    })?    .await?;     // Update the editor's state.    this.update(cx, |editor, cx| {        if !selections.is_empty() || pending_selection.is_some() {            editor.set_selections_from_remote(selections, pending_selection, cx);            editor.request_autoscroll_remotely(Autoscroll::newest(), cx);        } else if let Some(scroll_top_anchor) = scroll_top_anchor {            editor.set_scroll_anchor_remote(                ScrollAnchor {                    anchor: scroll_top_anchor,                    offset: point(message.scroll_x, message.scroll_y),                },                cx,            );        }    })?;    Ok(())} fn serialize_excerpt(    buffer_id: BufferId,    id: &ExcerptId,    range: &ExcerptRange<language::Anchor>,) -> Option<proto::Excerpt> {    Some(proto::Excerpt {        id: id.to_proto(),        buffer_id: buffer_id.into(),        context_start: Some(serialize_text_anchor(&range.context.start)),        context_end: Some(serialize_text_anchor(&range.context.end)),        primary_start: range            .primary            .as_ref()            .map(|r| serialize_text_anchor(&r.start)),        primary_end: range            .primary            .as_ref()            .map(|r| serialize_text_anchor(&r.end)),    })} fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {    proto::Selection {        id: selection.id as u64,        start: Some(serialize_anchor(&selection.start)),        end: Some(serialize_anchor(&selection.end)),        reversed: selection.reversed,    }} fn serialize_anchor(anchor: &Anchor) -> proto::EditorAnchor {    proto::EditorAnchor {        excerpt_id: anchor.excerpt_id.to_proto(),        anchor: Some(serialize_text_anchor(&anchor.text_anchor)),    }} fn deserialize_excerpt_range(excerpt: proto::Excerpt) -> Option<ExcerptRange<language::Anchor>> {    let context = {        let start = language::proto::deserialize_anchor(excerpt.context_start?)?;        let end = language::proto::deserialize_anchor(excerpt.context_end?)?;        start..end    };

Lines 1–500 of 1719.