1825 lines · 75.9 KB · Rust
RawDownload
use crate::thread::{    LastRestoreCheckpoint, MessageId, MessageSegment, RequestKind, Thread, ThreadError,    ThreadEvent, ThreadFeedback,};use crate::thread_store::ThreadStore;use crate::tool_use::{PendingToolUseStatus, ToolUse, ToolUseStatus};use crate::ui::{ContextPill, ToolReadyPopUp, ToolReadyPopupEvent};use crate::AssistantPanel;use assistant_settings::AssistantSettings;use collections::HashMap;use editor::{Editor, MultiBuffer};use gpui::{    linear_color_stop, linear_gradient, list, percentage, pulsating_between, AbsoluteLength,    Animation, AnimationExt, AnyElement, App, ClickEvent, DefiniteLength, EdgesRefinement, Empty,    Entity, Focusable, Hsla, Length, ListAlignment, ListOffset, ListState, MouseButton,    ScrollHandle, Stateful, StyleRefinement, Subscription, Task, TextStyleRefinement,    Transformation, UnderlineStyle, WeakEntity, WindowHandle,};use language::{Buffer, LanguageRegistry};use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role};use markdown::{Markdown, MarkdownStyle};use settings::Settings as _;use std::sync::Arc;use std::time::Duration;use theme::ThemeSettings;use ui::{prelude::*, Disclosure, IconButton, KeyBinding, Scrollbar, ScrollbarState, Tooltip};use util::ResultExt as _;use workspace::{OpenOptions, Workspace}; use crate::context_store::{refresh_context_store_text, ContextStore}; pub struct ActiveThread {    language_registry: Arc<LanguageRegistry>,    thread_store: Entity<ThreadStore>,    thread: Entity<Thread>,    context_store: Entity<ContextStore>,    workspace: WeakEntity<Workspace>,    save_thread_task: Option<Task<()>>,    messages: Vec<MessageId>,    list_state: ListState,    scrollbar_state: ScrollbarState,    rendered_messages_by_id: HashMap<MessageId, RenderedMessage>,    rendered_tool_use_labels: HashMap<LanguageModelToolUseId, Entity<Markdown>>,    editing_message: Option<(MessageId, EditMessageState)>,    expanded_tool_uses: HashMap<LanguageModelToolUseId, bool>,    expanded_thinking_segments: HashMap<(MessageId, usize), bool>,    last_error: Option<ThreadError>,    pop_ups: Vec<WindowHandle<ToolReadyPopUp>>,    _subscriptions: Vec<Subscription>,    pop_up_subscriptions: HashMap<WindowHandle<ToolReadyPopUp>, Vec<Subscription>>,} struct RenderedMessage {    language_registry: Arc<LanguageRegistry>,    segments: Vec<RenderedMessageSegment>,} impl RenderedMessage {    fn from_segments(        segments: &[MessageSegment],        language_registry: Arc<LanguageRegistry>,        window: &Window,        cx: &mut App,    ) -> Self {        let mut this = Self {            language_registry,            segments: Vec::with_capacity(segments.len()),        };        for segment in segments {            this.push_segment(segment, window, cx);        }        this    }     fn append_thinking(&mut self, text: &String, window: &Window, cx: &mut App) {        if let Some(RenderedMessageSegment::Thinking {            content,            scroll_handle,        }) = self.segments.last_mut()        {            content.update(cx, |markdown, cx| {                markdown.append(text, cx);            });            scroll_handle.scroll_to_bottom();        } else {            self.segments.push(RenderedMessageSegment::Thinking {                content: render_markdown(text.into(), self.language_registry.clone(), window, cx),                scroll_handle: ScrollHandle::default(),            });        }    }     fn append_text(&mut self, text: &String, window: &Window, cx: &mut App) {        if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() {            markdown.update(cx, |markdown, cx| markdown.append(text, cx));        } else {            self.segments                .push(RenderedMessageSegment::Text(render_markdown(                    SharedString::from(text),                    self.language_registry.clone(),                    window,                    cx,                )));        }    }     fn push_segment(&mut self, segment: &MessageSegment, window: &Window, cx: &mut App) {        let rendered_segment = match segment {            MessageSegment::Thinking(text) => RenderedMessageSegment::Thinking {                content: render_markdown(text.into(), self.language_registry.clone(), window, cx),                scroll_handle: ScrollHandle::default(),            },            MessageSegment::Text(text) => RenderedMessageSegment::Text(render_markdown(                text.into(),                self.language_registry.clone(),                window,                cx,            )),        };        self.segments.push(rendered_segment);    }} enum RenderedMessageSegment {    Thinking {        content: Entity<Markdown>,        scroll_handle: ScrollHandle,    },    Text(Entity<Markdown>),} fn render_markdown(    text: SharedString,    language_registry: Arc<LanguageRegistry>,    window: &Window,    cx: &mut App,) -> Entity<Markdown> {    let theme_settings = ThemeSettings::get_global(cx);    let colors = cx.theme().colors();    let ui_font_size = TextSize::Default.rems(cx);    let buffer_font_size = TextSize::Small.rems(cx);    let mut text_style = window.text_style();     text_style.refine(&TextStyleRefinement {        font_family: Some(theme_settings.ui_font.family.clone()),        font_fallbacks: theme_settings.ui_font.fallbacks.clone(),        font_features: Some(theme_settings.ui_font.features.clone()),        font_size: Some(ui_font_size.into()),        color: Some(cx.theme().colors().text),        ..Default::default()    });     let markdown_style = MarkdownStyle {        base_text_style: text_style,        syntax: cx.theme().syntax().clone(),        selection_background_color: cx.theme().players().local().selection,        code_block_overflow_x_scroll: true,        table_overflow_x_scroll: true,        code_block: StyleRefinement {            margin: EdgesRefinement {                top: Some(Length::Definite(rems(0.).into())),                left: Some(Length::Definite(rems(0.).into())),                right: Some(Length::Definite(rems(0.).into())),                bottom: Some(Length::Definite(rems(0.5).into())),            },            padding: EdgesRefinement {                top: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),                left: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),                right: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),                bottom: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),            },            background: Some(colors.editor_background.into()),            border_color: Some(colors.border_variant),            border_widths: EdgesRefinement {                top: Some(AbsoluteLength::Pixels(Pixels(1.))),                left: Some(AbsoluteLength::Pixels(Pixels(1.))),                right: Some(AbsoluteLength::Pixels(Pixels(1.))),                bottom: Some(AbsoluteLength::Pixels(Pixels(1.))),            },            text: Some(TextStyleRefinement {                font_family: Some(theme_settings.buffer_font.family.clone()),                font_fallbacks: theme_settings.buffer_font.fallbacks.clone(),                font_features: Some(theme_settings.buffer_font.features.clone()),                font_size: Some(buffer_font_size.into()),                ..Default::default()            }),            ..Default::default()        },        inline_code: TextStyleRefinement {            font_family: Some(theme_settings.buffer_font.family.clone()),            font_fallbacks: theme_settings.buffer_font.fallbacks.clone(),            font_features: Some(theme_settings.buffer_font.features.clone()),            font_size: Some(buffer_font_size.into()),            background_color: Some(colors.editor_foreground.opacity(0.1)),            ..Default::default()        },        link: TextStyleRefinement {            background_color: Some(colors.editor_foreground.opacity(0.025)),            underline: Some(UnderlineStyle {                color: Some(colors.text_accent.opacity(0.5)),                thickness: px(1.),                ..Default::default()            }),            ..Default::default()        },        ..Default::default()    };     cx.new(|cx| Markdown::new(text, markdown_style, Some(language_registry), None, cx))} struct EditMessageState {    editor: Entity<Editor>,} impl ActiveThread {    pub fn new(        thread: Entity<Thread>,        thread_store: Entity<ThreadStore>,        language_registry: Arc<LanguageRegistry>,        context_store: Entity<ContextStore>,        workspace: WeakEntity<Workspace>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Self {        let subscriptions = vec![            cx.observe(&thread, |_, _, cx| cx.notify()),            cx.subscribe_in(&thread, window, Self::handle_thread_event),        ];         let list_state = ListState::new(0, ListAlignment::Bottom, px(2048.), {            let this = cx.entity().downgrade();            move |ix, window: &mut Window, cx: &mut App| {                this.update(cx, |this, cx| this.render_message(ix, window, cx))                    .unwrap()            }        });         let mut this = Self {            language_registry,            thread_store,            thread: thread.clone(),            context_store,            workspace,            save_thread_task: None,            messages: Vec::new(),            rendered_messages_by_id: HashMap::default(),            rendered_tool_use_labels: HashMap::default(),            expanded_tool_uses: HashMap::default(),            expanded_thinking_segments: HashMap::default(),            list_state: list_state.clone(),            scrollbar_state: ScrollbarState::new(list_state),            editing_message: None,            last_error: None,            pop_ups: Vec::new(),            _subscriptions: subscriptions,            pop_up_subscriptions: HashMap::default(),        };         for message in thread.read(cx).messages().cloned().collect::<Vec<_>>() {            this.push_message(&message.id, &message.segments, window, cx);             for tool_use in thread.read(cx).tool_uses_for_message(message.id, cx) {                this.render_tool_use_label_markdown(                    tool_use.id.clone(),                    tool_use.ui_text.clone(),                    window,                    cx,                );            }        }         this    }     pub fn thread(&self) -> &Entity<Thread> {        &self.thread    }     pub fn is_empty(&self) -> bool {        self.messages.is_empty()    }     pub fn summary(&self, cx: &App) -> Option<SharedString> {        self.thread.read(cx).summary()    }     pub fn summary_or_default(&self, cx: &App) -> SharedString {        self.thread.read(cx).summary_or_default()    }     pub fn cancel_last_completion(&mut self, cx: &mut App) -> bool {        self.last_error.take();        self.thread            .update(cx, |thread, cx| thread.cancel_last_completion(cx))    }     pub fn last_error(&self) -> Option<ThreadError> {        self.last_error.clone()    }     pub fn clear_last_error(&mut self) {        self.last_error.take();    }     fn push_message(        &mut self,        id: &MessageId,        segments: &[MessageSegment],        window: &mut Window,        cx: &mut Context<Self>,    ) {        let old_len = self.messages.len();        self.messages.push(*id);        self.list_state.splice(old_len..old_len, 1);         let rendered_message =            RenderedMessage::from_segments(segments, self.language_registry.clone(), window, cx);        self.rendered_messages_by_id.insert(*id, rendered_message);        self.list_state.scroll_to(ListOffset {            item_ix: old_len,            offset_in_item: Pixels(0.0),        });    }     fn edited_message(        &mut self,        id: &MessageId,        segments: &[MessageSegment],        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {            return;        };        self.list_state.splice(index..index + 1, 1);        let rendered_message =            RenderedMessage::from_segments(segments, self.language_registry.clone(), window, cx);        self.rendered_messages_by_id.insert(*id, rendered_message);    }     fn deleted_message(&mut self, id: &MessageId) {        let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {            return;        };        self.messages.remove(index);        self.list_state.splice(index..index + 1, 0);        self.rendered_messages_by_id.remove(id);    }     fn render_tool_use_label_markdown(        &mut self,        tool_use_id: LanguageModelToolUseId,        tool_label: impl Into<SharedString>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        self.rendered_tool_use_labels.insert(            tool_use_id,            render_markdown(                tool_label.into(),                self.language_registry.clone(),                window,                cx,            ),        );    }     fn handle_thread_event(        &mut self,        _thread: &Entity<Thread>,        event: &ThreadEvent,        window: &mut Window,        cx: &mut Context<Self>,    ) {        match event {            ThreadEvent::ShowError(error) => {                self.last_error = Some(error.clone());            }            ThreadEvent::StreamedCompletion | ThreadEvent::SummaryChanged => {                self.save_thread(cx);            }            ThreadEvent::DoneStreaming => {                if !self.thread().read(cx).is_generating() {                    self.show_notification(                        "The assistant response has concluded.",                        IconName::Check,                        Color::Success,                        window,                        cx,                    );                }            }            ThreadEvent::ToolConfirmationNeeded => {                self.show_notification(                    "There's a tool confirmation needed.",                    IconName::Info,                    Color::Muted,                    window,                    cx,                );            }            ThreadEvent::StreamedAssistantText(message_id, text) => {                if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) {                    rendered_message.append_text(text, window, cx);                }            }            ThreadEvent::StreamedAssistantThinking(message_id, text) => {                if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) {                    rendered_message.append_thinking(text, window, cx);                }            }            ThreadEvent::MessageAdded(message_id) => {                if let Some(message_segments) = self                    .thread                    .read(cx)                    .message(*message_id)                    .map(|message| message.segments.clone())                {                    self.push_message(message_id, &message_segments, window, cx);                }                 self.save_thread(cx);                cx.notify();            }            ThreadEvent::MessageEdited(message_id) => {                if let Some(message_segments) = self                    .thread                    .read(cx)                    .message(*message_id)                    .map(|message| message.segments.clone())                {                    self.edited_message(message_id, &message_segments, window, cx);                }                 self.save_thread(cx);                cx.notify();            }            ThreadEvent::MessageDeleted(message_id) => {                self.deleted_message(message_id);                self.save_thread(cx);                cx.notify();            }            ThreadEvent::UsePendingTools => {                let tool_uses = self                    .thread                    .update(cx, |thread, cx| thread.use_pending_tools(cx));                 for tool_use in tool_uses {                    self.render_tool_use_label_markdown(                        tool_use.id.clone(),                        tool_use.ui_text.clone(),                        window,                        cx,                    );                }            }            ThreadEvent::ToolFinished {                pending_tool_use,                canceled,                ..            } => {                let canceled = *canceled;                if let Some(tool_use) = pending_tool_use {                    self.render_tool_use_label_markdown(                        tool_use.id.clone(),                        SharedString::from(tool_use.ui_text.clone()),                        window,                        cx,                    );                }                 if self.thread.read(cx).all_tools_finished() {                    let pending_refresh_buffers = self.thread.update(cx, |thread, cx| {                        thread.action_log().update(cx, |action_log, _cx| {                            action_log.take_stale_buffers_in_context()                        })                    });                     let context_update_task = if !pending_refresh_buffers.is_empty() {                        let refresh_task = refresh_context_store_text(                            self.context_store.clone(),                            &pending_refresh_buffers,                            cx,                        );                         cx.spawn(async move |this, cx| {                            let updated_context_ids = refresh_task.await;                             this.update(cx, |this, cx| {                                this.context_store.read_with(cx, |context_store, cx| {                                    context_store                                        .context()                                        .iter()                                        .filter(|context| {                                            updated_context_ids.contains(&context.id())                                        })                                        .flat_map(|context| context.snapshot(cx))                                        .collect()                                })

Lines 1–500 of 1825.