5349 lines · 199.9 KB · Rust
RawDownload
use crate::slash_command::file_command::codeblock_fence_for_path;use crate::slash_command_working_set::SlashCommandWorkingSet;use crate::{    assistant_settings::{AssistantDockPosition, AssistantSettings},    humanize_token_count,    prompt_library::open_prompt_library,    prompts::PromptBuilder,    slash_command::{        default_command::DefaultSlashCommand,        docs_command::{DocsSlashCommand, DocsSlashCommandArgs},        file_command, SlashCommandCompletionProvider,    },    slash_command_picker,    terminal_inline_assistant::TerminalInlineAssistant,    Assist, AssistantPatch, AssistantPatchStatus, CacheStatus, ConfirmCommand, Content, Context,    ContextEvent, ContextId, ContextStore, ContextStoreEvent, CopyCode, CycleMessageRole,    DeployHistory, DeployPromptLibrary, Edit, InlineAssistant, InsertDraggedFiles,    InsertIntoEditor, InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId,    MessageMetadata, MessageStatus, NewContext, ParsedSlashCommand, PendingSlashCommandStatus,    QuoteSelection, RemoteContextMetadata, RequestType, SavedContextMetadata, Split, ToggleFocus,    ToggleModelSelector,};use anyhow::Result;use assistant_slash_command::{SlashCommand, SlashCommandOutputSection};use assistant_tool::ToolWorkingSet;use client::{proto, zed_urls, Client, Status};use collections::{hash_map, BTreeSet, HashMap, HashSet};use editor::{    actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},    display_map::{        BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata,        CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,    },    scroll::{Autoscroll, AutoscrollStrategy},    Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt,    ToOffset as _, ToPoint,};use editor::{display_map::CreaseId, FoldPlaceholder};use fs::Fs;use futures::FutureExt;use gpui::{    canvas, div, img, percentage, point, prelude::*, pulsating_between, size, Action, Animation,    AnimationExt, AnyElement, AnyView, AppContext, AsyncWindowContext, ClipboardEntry,    ClipboardItem, CursorStyle, Empty, Entity, EventEmitter, ExternalPaths, FocusHandle,    FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ParentElement, Pixels,    Render, RenderImage, SharedString, Size, StatefulInteractiveElement, Styled, Subscription,    Task, Transformation, UpdateGlobal, View, WeakModel, WeakView,};use indexed_docs::IndexedDocsStore;use language::{    language_settings::SoftWrap, BufferSnapshot, LanguageRegistry, LspAdapterDelegate, ToOffset,};use language_model::{LanguageModelImage, LanguageModelToolUse};use language_model::{    LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Role,    ZED_CLOUD_PROVIDER_ID,};use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};use multi_buffer::MultiBufferRow;use picker::{Picker, PickerDelegate};use project::lsp_store::LocalLspAdapterDelegate;use project::{Project, Worktree};use rope::Point;use search::{buffer_search::DivRegistrar, BufferSearchBar};use serde::{Deserialize, Serialize};use settings::{update_settings_file, Settings};use smol::stream::StreamExt;use std::{    any::TypeId,    borrow::Cow,    cmp,    ops::{ControlFlow, Range},    path::PathBuf,    sync::Arc,    time::Duration,};use terminal_view::{terminal_panel::TerminalPanel, TerminalView};use text::SelectionGoal;use ui::{    prelude::*,    utils::{format_distance_from_now, DateTimeType},    Avatar, ButtonLike, ContextMenu, Disclosure, ElevationIndex, KeyBinding, ListItem,    ListItemSpacing, PopoverMenu, PopoverMenuHandle, TintColor, Tooltip,};use util::{maybe, ResultExt};use workspace::{    dock::{DockPosition, Panel, PanelEvent},    item::{self, FollowableItem, Item, ItemHandle},    notifications::NotificationId,    pane::{self, SaveIntent},    searchable::{SearchEvent, SearchableItem},    DraggedSelection, Pane, Save, ShowConfiguration, Toast, ToggleZoom, ToolbarItemEvent,    ToolbarItemLocation, ToolbarItemView, Workspace,};use workspace::{searchable::SearchableItemHandle, DraggedTab};use zed_actions::InlineAssist; pub fn init(cx: &mut AppContext) {    workspace::FollowableViewRegistry::register::<ContextEditor>(cx);    cx.observe_new_views(        |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {            workspace                .register_action(|workspace, _: &ToggleFocus, cx| {                    let settings = AssistantSettings::get_global(cx);                    if !settings.enabled {                        return;                    }                     workspace.toggle_panel_focus::<AssistantPanel>(cx);                })                .register_action(AssistantPanel::inline_assist)                .register_action(ContextEditor::quote_selection)                .register_action(ContextEditor::insert_selection)                .register_action(ContextEditor::copy_code)                .register_action(ContextEditor::insert_dragged_files)                .register_action(AssistantPanel::show_configuration)                .register_action(AssistantPanel::create_new_context)                .register_action(AssistantPanel::restart_context_servers);        },    )    .detach();     cx.observe_new_views(        |terminal_panel: &mut TerminalPanel, cx: &mut ViewContext<TerminalPanel>| {            let settings = AssistantSettings::get_global(cx);            terminal_panel.asssistant_enabled(settings.enabled, cx);        },    )    .detach();} pub enum AssistantPanelEvent {    ContextEdited,} pub struct AssistantPanel {    pane: View<Pane>,    workspace: WeakView<Workspace>,    width: Option<Pixels>,    height: Option<Pixels>,    project: Model<Project>,    context_store: Model<ContextStore>,    languages: Arc<LanguageRegistry>,    fs: Arc<dyn Fs>,    subscriptions: Vec<Subscription>,    model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,    model_summary_editor: View<Editor>,    authenticate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,    configuration_subscription: Option<Subscription>,    client_status: Option<client::Status>,    watch_client_status: Option<Task<()>>,    show_zed_ai_notice: bool,} #[derive(Clone)]enum ContextMetadata {    Remote(RemoteContextMetadata),    Saved(SavedContextMetadata),} struct SavedContextPickerDelegate {    store: Model<ContextStore>,    project: Model<Project>,    matches: Vec<ContextMetadata>,    selected_index: usize,} enum SavedContextPickerEvent {    Confirmed(ContextMetadata),} enum InlineAssistTarget {    Editor(View<Editor>, bool),    Terminal(View<TerminalView>),} impl EventEmitter<SavedContextPickerEvent> for Picker<SavedContextPickerDelegate> {} impl SavedContextPickerDelegate {    fn new(project: Model<Project>, store: Model<ContextStore>) -> Self {        Self {            project,            store,            matches: Vec::new(),            selected_index: 0,        }    }} impl PickerDelegate for SavedContextPickerDelegate {    type ListItem = ListItem;     fn match_count(&self) -> usize {        self.matches.len()    }     fn selected_index(&self) -> usize {        self.selected_index    }     fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {        self.selected_index = ix;    }     fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {        "Search...".into()    }     fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {        let search = self.store.read(cx).search(query, cx);        cx.spawn(|this, mut cx| async move {            let matches = search.await;            this.update(&mut cx, |this, cx| {                let host_contexts = this.delegate.store.read(cx).host_contexts();                this.delegate.matches = host_contexts                    .iter()                    .cloned()                    .map(ContextMetadata::Remote)                    .chain(matches.into_iter().map(ContextMetadata::Saved))                    .collect();                this.delegate.selected_index = 0;                cx.notify();            })            .ok();        })    }     fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {        if let Some(metadata) = self.matches.get(self.selected_index) {            cx.emit(SavedContextPickerEvent::Confirmed(metadata.clone()));        }    }     fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}     fn render_match(        &self,        ix: usize,        selected: bool,        cx: &mut ViewContext<Picker<Self>>,    ) -> Option<Self::ListItem> {        let context = self.matches.get(ix)?;        let item = match context {            ContextMetadata::Remote(context) => {                let host_user = self.project.read(cx).host().and_then(|collaborator| {                    self.project                        .read(cx)                        .user_store()                        .read(cx)                        .get_cached_user(collaborator.user_id)                });                div()                    .flex()                    .w_full()                    .justify_between()                    .gap_2()                    .child(                        h_flex().flex_1().overflow_x_hidden().child(                            Label::new(context.summary.clone().unwrap_or(DEFAULT_TAB_TITLE.into()))                                .size(LabelSize::Small),                        ),                    )                    .child(                        h_flex()                            .gap_2()                            .children(if let Some(host_user) = host_user {                                vec![                                    Avatar::new(host_user.avatar_uri.clone()).into_any_element(),                                    Label::new(format!("Shared by @{}", host_user.github_login))                                        .color(Color::Muted)                                        .size(LabelSize::Small)                                        .into_any_element(),                                ]                            } else {                                vec![Label::new("Shared by host")                                    .color(Color::Muted)                                    .size(LabelSize::Small)                                    .into_any_element()]                            }),                    )            }            ContextMetadata::Saved(context) => div()                .flex()                .w_full()                .justify_between()                .gap_2()                .child(                    h_flex()                        .flex_1()                        .child(Label::new(context.title.clone()).size(LabelSize::Small))                        .overflow_x_hidden(),                )                .child(                    Label::new(format_distance_from_now(                        DateTimeType::Local(context.mtime),                        false,                        true,                        true,                    ))                    .color(Color::Muted)                    .size(LabelSize::Small),                ),        };        Some(            ListItem::new(ix)                .inset(true)                .spacing(ListItemSpacing::Sparse)                .toggle_state(selected)                .child(item),        )    }} impl AssistantPanel {    pub fn load(        workspace: WeakView<Workspace>,        prompt_builder: Arc<PromptBuilder>,        cx: AsyncWindowContext,    ) -> Task<Result<View<Self>>> {        cx.spawn(|mut cx| async move {            let slash_commands = Arc::new(SlashCommandWorkingSet::default());            let tools = Arc::new(ToolWorkingSet::default());            let context_store = workspace                .update(&mut cx, |workspace, cx| {                    let project = workspace.project().clone();                    ContextStore::new(project, prompt_builder.clone(), slash_commands, tools, cx)                })?                .await?;             workspace.update(&mut cx, |workspace, cx| {                // TODO: deserialize state.                cx.new_view(|cx| Self::new(workspace, context_store, cx))            })        })    }     fn new(        workspace: &Workspace,        context_store: Model<ContextStore>,        cx: &mut ViewContext<Self>,    ) -> Self {        let model_selector_menu_handle = PopoverMenuHandle::default();        let model_summary_editor = cx.new_view(Editor::single_line);        let context_editor_toolbar = cx.new_view(|cx| {            ContextEditorToolbarItem::new(                workspace,                model_selector_menu_handle.clone(),                model_summary_editor.clone(),                cx,            )        });         let pane = cx.new_view(|cx| {            let mut pane = Pane::new(                workspace.weak_handle(),                workspace.project().clone(),                Default::default(),                None,                NewContext.boxed_clone(),                cx,            );             let project = workspace.project().clone();            pane.set_custom_drop_handle(cx, move |_, dropped_item, cx| {                let action = maybe!({                    if project.read(cx).is_local() {                        if let Some(paths) = dropped_item.downcast_ref::<ExternalPaths>() {                            return Some(InsertDraggedFiles::ExternalFiles(paths.paths().to_vec()));                        }                    }                     let project_paths = if let Some(tab) = dropped_item.downcast_ref::<DraggedTab>()                    {                        if &tab.pane == cx.view() {                            return None;                        }                        let item = tab.pane.read(cx).item_for_index(tab.ix);                        Some(                            item.and_then(|item| item.project_path(cx))                                .into_iter()                                .collect::<Vec<_>>(),                        )                    } else if let Some(selection) = dropped_item.downcast_ref::<DraggedSelection>()                    {                        Some(                            selection                                .items()                                .filter_map(|item| {                                    project.read(cx).path_for_entry(item.entry_id, cx)                                })                                .collect::<Vec<_>>(),                        )                    } else {                        None                    }?;                     let paths = project_paths                        .into_iter()                        .filter_map(|project_path| {                            let worktree = project                                .read(cx)                                .worktree_for_id(project_path.worktree_id, cx)?;                             let mut full_path = PathBuf::from(worktree.read(cx).root_name());                            full_path.push(&project_path.path);                            Some(full_path)                        })                        .collect::<Vec<_>>();                     Some(InsertDraggedFiles::ProjectPaths(paths))                });                 if let Some(action) = action {                    cx.dispatch_action(action.boxed_clone());                }                 ControlFlow::Break(())            });             pane.set_can_navigate(true, cx);            pane.display_nav_history_buttons(None);            pane.set_should_display_tab_bar(|_| true);            pane.set_render_tab_bar_buttons(cx, move |pane, cx| {                let focus_handle = pane.focus_handle(cx);                let left_children = IconButton::new("history", IconName::HistoryRerun)                    .icon_size(IconSize::Small)                    .on_click(cx.listener({                        let focus_handle = focus_handle.clone();                        move |_, _, cx| {                            focus_handle.focus(cx);                            cx.dispatch_action(DeployHistory.boxed_clone())                        }                    }))                    .tooltip({                        let focus_handle = focus_handle.clone();                        move |cx| {                            Tooltip::for_action_in(                                "Open History",                                &DeployHistory,                                &focus_handle,                                cx,                            )                        }                    })                    .toggle_state(                        pane.active_item()                            .map_or(false, |item| item.downcast::<ContextHistory>().is_some()),                    );                let _pane = cx.view().clone();                let right_children = h_flex()                    .gap(DynamicSpacing::Base02.rems(cx))                    .child(                        IconButton::new("new-chat", IconName::Plus)                            .icon_size(IconSize::Small)                            .on_click(                                cx.listener(|_, _, cx| {                                    cx.dispatch_action(NewContext.boxed_clone())                                }),                            )                            .tooltip(move |cx| {                                Tooltip::for_action_in("New Chat", &NewContext, &focus_handle, cx)                            }),                    )                    .child(                        PopoverMenu::new("assistant-panel-popover-menu")                            .trigger(                                IconButton::new("menu", IconName::EllipsisVertical)                                    .icon_size(IconSize::Small)                                    .tooltip(|cx| Tooltip::text("Toggle Assistant Menu", cx)),                            )                            .menu(move |cx| {                                let zoom_label = if _pane.read(cx).is_zoomed() {                                    "Zoom Out"                                } else {                                    "Zoom In"                                };                                let focus_handle = _pane.focus_handle(cx);                                Some(ContextMenu::build(cx, move |menu, _| {                                    menu.context(focus_handle.clone())                                        .action("New Chat", Box::new(NewContext))                                        .action("History", Box::new(DeployHistory))                                        .action("Prompt Library", Box::new(DeployPromptLibrary))                                        .action("Configure", Box::new(ShowConfiguration))                                        .action(zoom_label, Box::new(ToggleZoom))                                }))                            }),                    )                    .into_any_element()                    .into();                 (Some(left_children.into_any_element()), right_children)            });            pane.toolbar().update(cx, |toolbar, cx| {                toolbar.add_item(context_editor_toolbar.clone(), cx);                toolbar.add_item(cx.new_view(BufferSearchBar::new), cx)            });            pane        });         let subscriptions = vec![

Lines 1–500 of 5349.