1111 lines · 40.7 KB · Rust
RawDownload
use std::path::PathBuf;use std::sync::Arc; use anyhow::{anyhow, Result};use assistant_context_editor::{    make_lsp_adapter_delegate, render_remaining_tokens, AssistantPanelDelegate, ConfigurationError,    ContextEditor, SlashCommandCompletionProvider,};use assistant_settings::{AssistantDockPosition, AssistantSettings};use assistant_slash_command::SlashCommandWorkingSet;use assistant_tool::ToolWorkingSet; use client::zed_urls;use editor::Editor;use fs::Fs;use gpui::{    prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,    FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity,};use language::LanguageRegistry;use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};use project::Project;use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};use settings::{update_settings_file, Settings};use time::UtcOffset;use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip};use util::ResultExt as _;use workspace::dock::{DockPosition, Panel, PanelEvent};use workspace::Workspace;use zed_actions::assistant::{DeployPromptLibrary, ToggleFocus}; use crate::active_thread::ActiveThread;use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};use crate::history_store::{HistoryEntry, HistoryStore};use crate::message_editor::MessageEditor;use crate::thread::{Thread, ThreadError, ThreadId};use crate::thread_history::{PastContext, PastThread, ThreadHistory};use crate::thread_store::ThreadStore;use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory}; pub fn init(cx: &mut App) {    cx.observe_new(        |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {            workspace                .register_action(|workspace, _: &NewThread, window, cx| {                    if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {                        panel.update(cx, |panel, cx| panel.new_thread(window, cx));                        workspace.focus_panel::<AssistantPanel>(window, cx);                    }                })                .register_action(|workspace, _: &OpenHistory, window, cx| {                    if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {                        workspace.focus_panel::<AssistantPanel>(window, cx);                        panel.update(cx, |panel, cx| panel.open_history(window, cx));                    }                })                .register_action(|workspace, _: &NewPromptEditor, window, cx| {                    if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {                        workspace.focus_panel::<AssistantPanel>(window, cx);                        panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx));                    }                })                .register_action(|workspace, _: &OpenConfiguration, window, cx| {                    if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {                        workspace.focus_panel::<AssistantPanel>(window, cx);                        panel.update(cx, |panel, cx| panel.open_configuration(window, cx));                    }                });        },    )    .detach();} enum ActiveView {    Thread,    PromptEditor,    History,    Configuration,} pub struct AssistantPanel {    workspace: WeakEntity<Workspace>,    project: Entity<Project>,    fs: Arc<dyn Fs>,    language_registry: Arc<LanguageRegistry>,    thread_store: Entity<ThreadStore>,    thread: Entity<ActiveThread>,    message_editor: Entity<MessageEditor>,    context_store: Entity<assistant_context_editor::ContextStore>,    context_editor: Option<Entity<ContextEditor>>,    configuration: Option<Entity<AssistantConfiguration>>,    configuration_subscription: Option<Subscription>,    tools: Arc<ToolWorkingSet>,    local_timezone: UtcOffset,    active_view: ActiveView,    history_store: Entity<HistoryStore>,    history: Entity<ThreadHistory>,    new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,    width: Option<Pixels>,    height: Option<Pixels>,} impl AssistantPanel {    pub fn load(        workspace: WeakEntity<Workspace>,        prompt_builder: Arc<PromptBuilder>,        cx: AsyncWindowContext,    ) -> Task<Result<Entity<Self>>> {        cx.spawn(|mut cx| async move {            let tools = Arc::new(ToolWorkingSet::default());            log::info!("[assistant2-debug] initializing ThreadStore");            let thread_store = workspace.update(&mut cx, |workspace, cx| {                let project = workspace.project().clone();                ThreadStore::new(project, tools.clone(), cx)            })??;            log::info!("[assistant2-debug] finished initializing ThreadStore");             let slash_commands = Arc::new(SlashCommandWorkingSet::default());            log::info!("[assistant2-debug] initializing ContextStore");            let context_store = workspace                .update(&mut cx, |workspace, cx| {                    let project = workspace.project().clone();                    assistant_context_editor::ContextStore::new(                        project,                        prompt_builder.clone(),                        slash_commands,                        cx,                    )                })?                .await?;            log::info!("[assistant2-debug] finished initializing ContextStore");             workspace.update_in(&mut cx, |workspace, window, cx| {                cx.new(|cx| Self::new(workspace, thread_store, context_store, tools, window, cx))            })        })    }     fn new(        workspace: &Workspace,        thread_store: Entity<ThreadStore>,        context_store: Entity<assistant_context_editor::ContextStore>,        tools: Arc<ToolWorkingSet>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Self {        log::info!("[assistant2-debug] AssistantPanel::new");        let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));        let fs = workspace.app_state().fs.clone();        let project = workspace.project().clone();        let language_registry = project.read(cx).languages().clone();        let workspace = workspace.weak_handle();        let weak_self = cx.entity().downgrade();         let message_editor = cx.new(|cx| {            MessageEditor::new(                fs.clone(),                workspace.clone(),                thread_store.downgrade(),                thread.clone(),                window,                cx,            )        });         let history_store =            cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx));         Self {            active_view: ActiveView::Thread,            workspace: workspace.clone(),            project,            fs: fs.clone(),            language_registry: language_registry.clone(),            thread_store: thread_store.clone(),            thread: cx.new(|cx| {                ActiveThread::new(                    thread.clone(),                    thread_store.clone(),                    workspace,                    language_registry,                    tools.clone(),                    window,                    cx,                )            }),            message_editor,            context_store,            context_editor: None,            configuration: None,            configuration_subscription: None,            tools,            local_timezone: UtcOffset::from_whole_seconds(                chrono::Local::now().offset().local_minus_utc(),            )            .unwrap(),            history_store: history_store.clone(),            history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, cx)),            new_item_context_menu_handle: PopoverMenuHandle::default(),            width: None,            height: None,        }    }     pub fn toggle_focus(        workspace: &mut Workspace,        _: &ToggleFocus,        window: &mut Window,        cx: &mut Context<Workspace>,    ) {        let settings = AssistantSettings::get_global(cx);        if !settings.enabled {            return;        }         workspace.toggle_panel_focus::<Self>(window, cx);    }     pub(crate) fn local_timezone(&self) -> UtcOffset {        self.local_timezone    }     pub(crate) fn thread_store(&self) -> &Entity<ThreadStore> {        &self.thread_store    }     fn cancel(        &mut self,        _: &editor::actions::Cancel,        _window: &mut Window,        cx: &mut Context<Self>,    ) {        self.thread            .update(cx, |thread, cx| thread.cancel_last_completion(cx));    }     fn new_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {        let thread = self            .thread_store            .update(cx, |this, cx| this.create_thread(cx));         self.active_view = ActiveView::Thread;        self.thread = cx.new(|cx| {            ActiveThread::new(                thread.clone(),                self.thread_store.clone(),                self.workspace.clone(),                self.language_registry.clone(),                self.tools.clone(),                window,                cx,            )        });        self.message_editor = cx.new(|cx| {            MessageEditor::new(                self.fs.clone(),                self.workspace.clone(),                self.thread_store.downgrade(),                thread,                window,                cx,            )        });        self.message_editor.focus_handle(cx).focus(window);    }     fn new_prompt_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {        self.active_view = ActiveView::PromptEditor;         let context = self            .context_store            .update(cx, |context_store, cx| context_store.create(cx));        let lsp_adapter_delegate = make_lsp_adapter_delegate(&self.project, cx)            .log_err()            .flatten();         self.context_editor = Some(cx.new(|cx| {            let mut editor = ContextEditor::for_context(                context,                self.fs.clone(),                self.workspace.clone(),                self.project.clone(),                lsp_adapter_delegate,                window,                cx,            );            editor.insert_default_prompt(window, cx);            editor        }));         if let Some(context_editor) = self.context_editor.as_ref() {            context_editor.focus_handle(cx).focus(window);        }    }     fn deploy_prompt_library(        &mut self,        _: &DeployPromptLibrary,        _window: &mut Window,        cx: &mut Context<Self>,    ) {        open_prompt_library(            self.language_registry.clone(),            Box::new(PromptLibraryInlineAssist::new(self.workspace.clone())),            Arc::new(|| {                Box::new(SlashCommandCompletionProvider::new(                    Arc::new(SlashCommandWorkingSet::default()),                    None,                    None,                ))            }),            cx,        )        .detach_and_log_err(cx);    }     fn open_history(&mut self, window: &mut Window, cx: &mut Context<Self>) {        self.thread_store            .update(cx, |thread_store, cx| thread_store.reload(cx))            .detach_and_log_err(cx);        self.active_view = ActiveView::History;        self.history.focus_handle(cx).focus(window);        cx.notify();    }     pub(crate) fn open_saved_prompt_editor(        &mut self,        path: PathBuf,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Task<Result<()>> {        let context = self            .context_store            .update(cx, |store, cx| store.open_local_context(path.clone(), cx));        let fs = self.fs.clone();        let project = self.project.clone();        let workspace = self.workspace.clone();         let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();         cx.spawn_in(window, |this, mut cx| async move {            let context = context.await?;            this.update_in(&mut cx, |this, window, cx| {                let editor = cx.new(|cx| {                    ContextEditor::for_context(                        context,                        fs,                        workspace,                        project,                        lsp_adapter_delegate,                        window,                        cx,                    )                });                this.active_view = ActiveView::PromptEditor;                this.context_editor = Some(editor);                 anyhow::Ok(())            })??;            Ok(())        })    }     pub(crate) fn open_thread(        &mut self,        thread_id: &ThreadId,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Task<Result<()>> {        let open_thread_task = self            .thread_store            .update(cx, |this, cx| this.open_thread(thread_id, cx));         cx.spawn_in(window, |this, mut cx| async move {            let thread = open_thread_task.await?;            this.update_in(&mut cx, |this, window, cx| {                this.active_view = ActiveView::Thread;                this.thread = cx.new(|cx| {                    ActiveThread::new(                        thread.clone(),                        this.thread_store.clone(),                        this.workspace.clone(),                        this.language_registry.clone(),                        this.tools.clone(),                        window,                        cx,                    )                });                this.message_editor = cx.new(|cx| {                    MessageEditor::new(                        this.fs.clone(),                        this.workspace.clone(),                        this.thread_store.downgrade(),                        thread,                        window,                        cx,                    )                });                this.message_editor.focus_handle(cx).focus(window);            })        })    }     pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) {        self.active_view = ActiveView::Configuration;        self.configuration = Some(cx.new(|cx| AssistantConfiguration::new(window, cx)));         if let Some(configuration) = self.configuration.as_ref() {            self.configuration_subscription = Some(cx.subscribe_in(                configuration,                window,                Self::handle_assistant_configuration_event,            ));             configuration.focus_handle(cx).focus(window);        }    }     fn handle_assistant_configuration_event(        &mut self,        _entity: &Entity<AssistantConfiguration>,        event: &AssistantConfigurationEvent,        window: &mut Window,        cx: &mut Context<Self>,    ) {        match event {            AssistantConfigurationEvent::NewThread(provider) => {                if LanguageModelRegistry::read_global(cx)                    .active_provider()                    .map_or(true, |active_provider| {                        active_provider.id() != provider.id()                    })                {                    if let Some(model) = provider.default_model(cx) {                        update_settings_file::<AssistantSettings>(                            self.fs.clone(),                            cx,                            move |settings, _| settings.set_model(model),                        );                    }                }                 self.new_thread(window, cx);            }        }    }     pub(crate) fn active_thread(&self, cx: &App) -> Entity<Thread> {        self.thread.read(cx).thread().clone()    }     pub(crate) fn delete_thread(&mut self, thread_id: &ThreadId, cx: &mut Context<Self>) {        self.thread_store            .update(cx, |this, cx| this.delete_thread(thread_id, cx))            .detach_and_log_err(cx);    }     pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> {        self.context_editor.clone()    }     pub(crate) fn delete_context(&mut self, path: PathBuf, cx: &mut Context<Self>) {        self.context_store            .update(cx, |this, cx| this.delete_local_context(path, cx))            .detach_and_log_err(cx);    }} impl Focusable for AssistantPanel {    fn focus_handle(&self, cx: &App) -> FocusHandle {        match self.active_view {            ActiveView::Thread => self.message_editor.focus_handle(cx),            ActiveView::History => self.history.focus_handle(cx),            ActiveView::PromptEditor => {                if let Some(context_editor) = self.context_editor.as_ref() {                    context_editor.focus_handle(cx)                } else {                    cx.focus_handle()                }            }            ActiveView::Configuration => {                if let Some(configuration) = self.configuration.as_ref() {                    configuration.focus_handle(cx)                } else {                    cx.focus_handle()                }            }        }    }} impl EventEmitter<PanelEvent> for AssistantPanel {} impl Panel for AssistantPanel {    fn persistent_name() -> &'static str {        "AssistantPanel2"    }     fn position(&self, _window: &Window, cx: &App) -> DockPosition {        match AssistantSettings::get_global(cx).dock {

Lines 1–500 of 1111.