use std::cmp;use std::mem;use std::ops::Range;use std::rc::Rc;use std::sync::Arc; use anyhow::{Context as _, Result};use assistant_settings::AssistantSettings;use client::telemetry::Telemetry;use collections::{hash_map, HashMap, HashSet, VecDeque};use editor::{ actions::SelectAll, display_map::{ BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock, ToDisplayPoint, }, Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange, GutterDimensions, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint,};use feature_flags::{Assistant2FeatureFlag, FeatureFlagViewExt as _};use fs::Fs;use gpui::{ point, App, Context, Entity, Focusable, Global, HighlightStyle, Subscription, Task, UpdateGlobal, WeakEntity, Window,};use language::{Buffer, Point, Selection, TransactionId};use language_model::{report_assistant_event, LanguageModelRegistry};use multi_buffer::MultiBufferRow;use parking_lot::Mutex;use project::{CodeAction, ProjectTransaction};use prompt_store::PromptBuilder;use settings::{Settings, SettingsStore};use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};use terminal_view::{terminal_panel::TerminalPanel, TerminalView};use text::{OffsetRangeExt, ToPoint as _};use ui::prelude::*;use util::RangeExt;use util::ResultExt;use workspace::{dock::Panel, ShowConfiguration};use workspace::{notifications::NotificationId, ItemHandle, Toast, Workspace}; use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent};use crate::context_store::ContextStore;use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent};use crate::terminal_inline_assistant::TerminalInlineAssistant;use crate::thread_store::ThreadStore;use crate::AssistantPanel; pub fn init( fs: Arc<dyn Fs>, prompt_builder: Arc<PromptBuilder>, telemetry: Arc<Telemetry>, cx: &mut App,) { cx.set_global(InlineAssistant::new(fs, prompt_builder, telemetry)); cx.observe_new(|_workspace: &mut Workspace, window, cx| { let Some(window) = window else { return; }; let workspace = cx.entity().clone(); InlineAssistant::update_global(cx, |inline_assistant, cx| { inline_assistant.register_workspace(&workspace, window, cx) }); cx.observe_flag::<Assistant2FeatureFlag, _>(window, { |is_assistant2_enabled, _workspace, _window, cx| { InlineAssistant::update_global(cx, |inline_assistant, _cx| { inline_assistant.is_assistant2_enabled = is_assistant2_enabled; }); } }) .detach(); }) .detach();} const PROMPT_HISTORY_MAX_LEN: usize = 20; enum InlineAssistTarget { Editor(Entity<Editor>), Terminal(Entity<TerminalView>),} pub struct InlineAssistant { next_assist_id: InlineAssistId, next_assist_group_id: InlineAssistGroupId, assists: HashMap<InlineAssistId, InlineAssist>, assists_by_editor: HashMap<WeakEntity<Editor>, EditorInlineAssists>, assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>, confirmed_assists: HashMap<InlineAssistId, Entity<CodegenAlternative>>, prompt_history: VecDeque<String>, prompt_builder: Arc<PromptBuilder>, telemetry: Arc<Telemetry>, fs: Arc<dyn Fs>, is_assistant2_enabled: bool,} impl Global for InlineAssistant {} impl InlineAssistant { pub fn new( fs: Arc<dyn Fs>, prompt_builder: Arc<PromptBuilder>, telemetry: Arc<Telemetry>, ) -> Self { Self { next_assist_id: InlineAssistId::default(), next_assist_group_id: InlineAssistGroupId::default(), assists: HashMap::default(), assists_by_editor: HashMap::default(), assist_groups: HashMap::default(), confirmed_assists: HashMap::default(), prompt_history: VecDeque::default(), prompt_builder, telemetry, fs, is_assistant2_enabled: false, } } pub fn register_workspace( &mut self, workspace: &Entity<Workspace>, window: &mut Window, cx: &mut App, ) { window .subscribe(workspace, cx, |workspace, event, window, cx| { Self::update_global(cx, |this, cx| { this.handle_workspace_event(workspace, event, window, cx) }); }) .detach(); let workspace = workspace.downgrade(); cx.observe_global::<SettingsStore>(move |cx| { let Some(workspace) = workspace.upgrade() else { return; }; let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else { return; }; let enabled = AssistantSettings::get_global(cx).enabled; terminal_panel.update(cx, |terminal_panel, cx| { terminal_panel.set_assistant_enabled(enabled, cx) }); }) .detach(); } fn handle_workspace_event( &mut self, workspace: Entity<Workspace>, event: &workspace::Event, window: &mut Window, cx: &mut App, ) { match event { workspace::Event::UserSavedItem { item, .. } => { // When the user manually saves an editor, automatically accepts all finished transformations. if let Some(editor) = item.upgrade().and_then(|item| item.act_as::<Editor>(cx)) { if let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) { for assist_id in editor_assists.assist_ids.clone() { let assist = &self.assists[&assist_id]; if let CodegenStatus::Done = assist.codegen.read(cx).status(cx) { self.finish_assist(assist_id, false, window, cx) } } } } } workspace::Event::ItemAdded { item } => { self.register_workspace_item(&workspace, item.as_ref(), window, cx); } _ => (), } } fn register_workspace_item( &mut self, workspace: &Entity<Workspace>, item: &dyn ItemHandle, window: &mut Window, cx: &mut App, ) { let is_assistant2_enabled = self.is_assistant2_enabled; if let Some(editor) = item.act_as::<Editor>(cx) { editor.update(cx, |editor, cx| { if is_assistant2_enabled { let thread_store = workspace .read(cx) .panel::<AssistantPanel>(cx) .map(|assistant_panel| assistant_panel.read(cx).thread_store().downgrade()); editor.add_code_action_provider( Rc::new(AssistantCodeActionProvider { editor: cx.entity().downgrade(), workspace: workspace.downgrade(), thread_store, }), window, cx, ); // Remove the Assistant1 code action provider, as it still might be registered. editor.remove_code_action_provider("assistant".into(), window, cx); } else { editor.remove_code_action_provider( ASSISTANT_CODE_ACTION_PROVIDER_ID.into(), window, cx, ); } }); } } pub fn inline_assist( workspace: &mut Workspace, _action: &zed_actions::assistant::InlineAssist, window: &mut Window, cx: &mut Context<Workspace>, ) { let settings = AssistantSettings::get_global(cx); if !settings.enabled { return; } let Some(inline_assist_target) = Self::resolve_inline_assist_target( workspace, workspace.panel::<AssistantPanel>(cx), window, cx, ) else { return; }; let is_authenticated = || { LanguageModelRegistry::read_global(cx) .active_provider() .map_or(false, |provider| provider.is_authenticated(cx)) }; let thread_store = workspace .panel::<AssistantPanel>(cx) .map(|assistant_panel| assistant_panel.read(cx).thread_store().downgrade()); let handle_assist = |window: &mut Window, cx: &mut Context<Workspace>| match inline_assist_target { InlineAssistTarget::Editor(active_editor) => { InlineAssistant::update_global(cx, |assistant, cx| { assistant.assist( &active_editor, cx.entity().downgrade(), thread_store, window, cx, ) }) } InlineAssistTarget::Terminal(active_terminal) => { TerminalInlineAssistant::update_global(cx, |assistant, cx| { assistant.assist( &active_terminal, cx.entity().downgrade(), thread_store, window, cx, ) }) } }; if is_authenticated() { handle_assist(window, cx); } else { cx.spawn_in(window, |_workspace, mut cx| async move { let Some(task) = cx.update(|_, cx| { LanguageModelRegistry::read_global(cx) .active_provider() .map_or(None, |provider| Some(provider.authenticate(cx))) })? else { let answer = cx .prompt( gpui::PromptLevel::Warning, "No language model provider configured", None, &["Configure", "Cancel"], ) .await .ok(); if let Some(answer) = answer { if answer == 0 { cx.update(|window, cx| { window.dispatch_action(Box::new(ShowConfiguration), cx) }) .ok(); } } return Ok(()); }; task.await?; anyhow::Ok(()) }) .detach_and_log_err(cx); if is_authenticated() { handle_assist(window, cx); } } } pub fn assist( &mut self, editor: &Entity<Editor>, workspace: WeakEntity<Workspace>, thread_store: Option<WeakEntity<ThreadStore>>, window: &mut Window, cx: &mut App, ) { let (snapshot, initial_selections) = editor.update(cx, |editor, cx| { ( editor.buffer().read(cx).snapshot(cx), editor.selections.all::<Point>(cx), ) }); let mut selections = Vec::<Selection<Point>>::new(); let mut newest_selection = None; for mut selection in initial_selections { if selection.end > selection.start { selection.start.column = 0; // If the selection ends at the start of the line, we don't want to include it. if selection.end.column == 0 { selection.end.row -= 1; } selection.end.column = snapshot.line_len(MultiBufferRow(selection.end.row)); } if let Some(prev_selection) = selections.last_mut() { if selection.start <= prev_selection.end { prev_selection.end = selection.end; continue; } } let latest_selection = newest_selection.get_or_insert_with(|| selection.clone()); if selection.id > latest_selection.id { *latest_selection = selection.clone(); } selections.push(selection); } let newest_selection = newest_selection.unwrap(); let mut codegen_ranges = Vec::new(); for (buffer, buffer_range, excerpt_id) in snapshot.ranges_to_buffer_ranges(selections.iter().map(|selection| { snapshot.anchor_before(selection.start)..snapshot.anchor_after(selection.end) })) { let anchor_range = Anchor::range_in_buffer( excerpt_id, buffer.remote_id(), buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end), ); codegen_ranges.push(anchor_range); if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() { self.telemetry.report_assistant_event(AssistantEvent { conversation_id: None, kind: AssistantKind::Inline, phase: AssistantPhase::Invoked, message_id: None, model: model.telemetry_id(), model_provider: model.provider_id().to_string(), response_latency: None, error_message: None, language_name: buffer.language().map(|language| language.name().to_proto()), }); } } let assist_group_id = self.next_assist_group_id.post_inc(); let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(String::new(), cx)), cx)); let mut assists = Vec::new(); let mut assist_to_focus = None; for range in codegen_ranges { let assist_id = self.next_assist_id.post_inc(); let context_store = cx.new(|_cx| ContextStore::new(workspace.clone())); let codegen = cx.new(|cx| { BufferCodegen::new( editor.read(cx).buffer().clone(), range.clone(), None, context_store.clone(), self.telemetry.clone(), self.prompt_builder.clone(), cx, ) }); let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default())); let prompt_editor = cx.new(|cx| { PromptEditor::new_buffer( assist_id, gutter_dimensions.clone(), self.prompt_history.clone(), prompt_buffer.clone(), codegen.clone(), self.fs.clone(), context_store, workspace.clone(), thread_store.clone(), window, cx, ) }); if assist_to_focus.is_none() { let focus_assist = if newest_selection.reversed { range.start.to_point(&snapshot) == newest_selection.start } else { range.end.to_point(&snapshot) == newest_selection.end }; if focus_assist { assist_to_focus = Some(assist_id); } } let [prompt_block_id, end_block_id] = self.insert_assist_blocks(editor, &range, &prompt_editor, cx); assists.push(( assist_id, range, prompt_editor, prompt_block_id, end_block_id, )); } let editor_assists = self .assists_by_editor .entry(editor.downgrade()) .or_insert_with(|| EditorInlineAssists::new(&editor, window, cx)); let mut assist_group = InlineAssistGroup::new(); for (assist_id, range, prompt_editor, prompt_block_id, end_block_id) in assists { let codegen = prompt_editor.read(cx).codegen().clone(); self.assists.insert( assist_id, InlineAssist::new( assist_id, assist_group_id, editor, &prompt_editor, prompt_block_id, end_block_id, range, codegen, workspace.clone(), window, cx, ), ); assist_group.assist_ids.push(assist_id); editor_assists.assist_ids.push(assist_id); } self.assist_groups.insert(assist_group_id, assist_group); if let Some(assist_id) = assist_to_focus { self.focus_assist(assist_id, window, cx); } } #[allow(clippy::too_many_arguments)] pub fn suggest_assist( &mut self, editor: &Entity<Editor>, mut range: Range<Anchor>, initial_prompt: String, initial_transaction_id: Option<TransactionId>, focus: bool, workspace: WeakEntity<Workspace>, thread_store: Option<WeakEntity<ThreadStore>>, window: &mut Window, cx: &mut App, ) -> InlineAssistId { let assist_group_id = self.next_assist_group_id.post_inc(); let prompt_buffer = cx.new(|cx| Buffer::local(&initial_prompt, cx)); let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(prompt_buffer, cx)); let assist_id = self.next_assist_id.post_inc(); Lines 1–500 of 1831. Load more
use std::cmp; use std::mem; use std::ops::Range; use std::rc::Rc; use std::sync::Arc; use anyhow::{Context as _, Result}; use assistant_settings::AssistantSettings; use client::telemetry::Telemetry; use collections::{hash_map, HashMap, HashSet, VecDeque}; use editor::{ actions::SelectAll, display_map::{ BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock, ToDisplayPoint, }, Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange, GutterDimensions, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint, }; use feature_flags::{Assistant2FeatureFlag, FeatureFlagViewExt as _}; use fs::Fs; use gpui::{ point, App, Context, Entity, Focusable, Global, HighlightStyle, Subscription, Task, UpdateGlobal, WeakEntity, Window, }; use language::{Buffer, Point, Selection, TransactionId}; use language_model::{report_assistant_event, LanguageModelRegistry}; use multi_buffer::MultiBufferRow; use parking_lot::Mutex; use project::{CodeAction, ProjectTransaction}; use prompt_store::PromptBuilder; use settings::{Settings, SettingsStore}; use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase}; use terminal_view::{terminal_panel::TerminalPanel, TerminalView}; use text::{OffsetRangeExt, ToPoint as _}; use ui::prelude::*; use util::RangeExt; use util::ResultExt; use workspace::{dock::Panel, ShowConfiguration}; use workspace::{notifications::NotificationId, ItemHandle, Toast, Workspace}; use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent}; use crate::context_store::ContextStore; use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent}; use crate::terminal_inline_assistant::TerminalInlineAssistant; use crate::thread_store::ThreadStore; use crate::AssistantPanel; pub fn init( fs: Arc<dyn Fs>, prompt_builder: Arc<PromptBuilder>, telemetry: Arc<Telemetry>, cx: &mut App, ) { cx.set_global(InlineAssistant::new(fs, prompt_builder, telemetry)); cx.observe_new(|_workspace: &mut Workspace, window, cx| { let Some(window) = window else { return; }; let workspace = cx.entity().clone(); InlineAssistant::update_global(cx, |inline_assistant, cx| { inline_assistant.register_workspace(&workspace, window, cx) }); cx.observe_flag::<Assistant2FeatureFlag, _>(window, { |is_assistant2_enabled, _workspace, _window, cx| { InlineAssistant::update_global(cx, |inline_assistant, _cx| { inline_assistant.is_assistant2_enabled = is_assistant2_enabled; }); } }) .detach(); }) .detach(); } const PROMPT_HISTORY_MAX_LEN: usize = 20; enum InlineAssistTarget { Editor(Entity<Editor>), Terminal(Entity<TerminalView>), } pub struct InlineAssistant { next_assist_id: InlineAssistId, next_assist_group_id: InlineAssistGroupId, assists: HashMap<InlineAssistId, InlineAssist>, assists_by_editor: HashMap<WeakEntity<Editor>, EditorInlineAssists>, assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>, confirmed_assists: HashMap<InlineAssistId, Entity<CodegenAlternative>>, prompt_history: VecDeque<String>, prompt_builder: Arc<PromptBuilder>, telemetry: Arc<Telemetry>, fs: Arc<dyn Fs>, is_assistant2_enabled: bool, } impl Global for InlineAssistant {} impl InlineAssistant { pub fn new( fs: Arc<dyn Fs>, prompt_builder: Arc<PromptBuilder>, telemetry: Arc<Telemetry>, ) -> Self { Self { next_assist_id: InlineAssistId::default(), next_assist_group_id: InlineAssistGroupId::default(), assists: HashMap::default(), assists_by_editor: HashMap::default(), assist_groups: HashMap::default(), confirmed_assists: HashMap::default(), prompt_history: VecDeque::default(), prompt_builder, telemetry, fs, is_assistant2_enabled: false, } } pub fn register_workspace( &mut self, workspace: &Entity<Workspace>, window: &mut Window, cx: &mut App, ) { window .subscribe(workspace, cx, |workspace, event, window, cx| { Self::update_global(cx, |this, cx| { this.handle_workspace_event(workspace, event, window, cx) }); }) .detach(); let workspace = workspace.downgrade(); cx.observe_global::<SettingsStore>(move |cx| { let Some(workspace) = workspace.upgrade() else { return; }; let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else { return; }; let enabled = AssistantSettings::get_global(cx).enabled; terminal_panel.update(cx, |terminal_panel, cx| { terminal_panel.set_assistant_enabled(enabled, cx) }); }) .detach(); } fn handle_workspace_event( &mut self, workspace: Entity<Workspace>, event: &workspace::Event, window: &mut Window, cx: &mut App, ) { match event { workspace::Event::UserSavedItem { item, .. } => { // When the user manually saves an editor, automatically accepts all finished transformations. if let Some(editor) = item.upgrade().and_then(|item| item.act_as::<Editor>(cx)) { if let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) { for assist_id in editor_assists.assist_ids.clone() { let assist = &self.assists[&assist_id]; if let CodegenStatus::Done = assist.codegen.read(cx).status(cx) { self.finish_assist(assist_id, false, window, cx) } } } } } workspace::Event::ItemAdded { item } => { self.register_workspace_item(&workspace, item.as_ref(), window, cx); } _ => (), } } fn register_workspace_item( &mut self, workspace: &Entity<Workspace>, item: &dyn ItemHandle, window: &mut Window, cx: &mut App, ) { let is_assistant2_enabled = self.is_assistant2_enabled; if let Some(editor) = item.act_as::<Editor>(cx) { editor.update(cx, |editor, cx| { if is_assistant2_enabled { let thread_store = workspace .read(cx) .panel::<AssistantPanel>(cx) .map(|assistant_panel| assistant_panel.read(cx).thread_store().downgrade()); editor.add_code_action_provider( Rc::new(AssistantCodeActionProvider { editor: cx.entity().downgrade(), workspace: workspace.downgrade(), thread_store, }), window, cx, ); // Remove the Assistant1 code action provider, as it still might be registered. editor.remove_code_action_provider("assistant".into(), window, cx); } else { editor.remove_code_action_provider( ASSISTANT_CODE_ACTION_PROVIDER_ID.into(), window, cx, ); } }); } } pub fn inline_assist( workspace: &mut Workspace, _action: &zed_actions::assistant::InlineAssist, window: &mut Window, cx: &mut Context<Workspace>, ) { let settings = AssistantSettings::get_global(cx); if !settings.enabled { return; } let Some(inline_assist_target) = Self::resolve_inline_assist_target( workspace, workspace.panel::<AssistantPanel>(cx), window, cx, ) else { return; }; let is_authenticated = || { LanguageModelRegistry::read_global(cx) .active_provider() .map_or(false, |provider| provider.is_authenticated(cx)) }; let thread_store = workspace .panel::<AssistantPanel>(cx) .map(|assistant_panel| assistant_panel.read(cx).thread_store().downgrade()); let handle_assist = |window: &mut Window, cx: &mut Context<Workspace>| match inline_assist_target { InlineAssistTarget::Editor(active_editor) => { InlineAssistant::update_global(cx, |assistant, cx| { assistant.assist( &active_editor, cx.entity().downgrade(), thread_store, window, cx, ) }) } InlineAssistTarget::Terminal(active_terminal) => { TerminalInlineAssistant::update_global(cx, |assistant, cx| { assistant.assist( &active_terminal, cx.entity().downgrade(), thread_store, window, cx, ) }) } }; if is_authenticated() { handle_assist(window, cx); } else { cx.spawn_in(window, |_workspace, mut cx| async move { let Some(task) = cx.update(|_, cx| { LanguageModelRegistry::read_global(cx) .active_provider() .map_or(None, |provider| Some(provider.authenticate(cx))) })? else { let answer = cx .prompt( gpui::PromptLevel::Warning, "No language model provider configured", None, &["Configure", "Cancel"], ) .await .ok(); if let Some(answer) = answer { if answer == 0 { cx.update(|window, cx| { window.dispatch_action(Box::new(ShowConfiguration), cx) }) .ok(); } } return Ok(()); }; task.await?; anyhow::Ok(()) }) .detach_and_log_err(cx); if is_authenticated() { handle_assist(window, cx); } } } pub fn assist( &mut self, editor: &Entity<Editor>, workspace: WeakEntity<Workspace>, thread_store: Option<WeakEntity<ThreadStore>>, window: &mut Window, cx: &mut App, ) { let (snapshot, initial_selections) = editor.update(cx, |editor, cx| { ( editor.buffer().read(cx).snapshot(cx), editor.selections.all::<Point>(cx), ) }); let mut selections = Vec::<Selection<Point>>::new(); let mut newest_selection = None; for mut selection in initial_selections { if selection.end > selection.start { selection.start.column = 0; // If the selection ends at the start of the line, we don't want to include it. if selection.end.column == 0 { selection.end.row -= 1; } selection.end.column = snapshot.line_len(MultiBufferRow(selection.end.row)); } if let Some(prev_selection) = selections.last_mut() { if selection.start <= prev_selection.end { prev_selection.end = selection.end; continue; } } let latest_selection = newest_selection.get_or_insert_with(|| selection.clone()); if selection.id > latest_selection.id { *latest_selection = selection.clone(); } selections.push(selection); } let newest_selection = newest_selection.unwrap(); let mut codegen_ranges = Vec::new(); for (buffer, buffer_range, excerpt_id) in snapshot.ranges_to_buffer_ranges(selections.iter().map(|selection| { snapshot.anchor_before(selection.start)..snapshot.anchor_after(selection.end) })) { let anchor_range = Anchor::range_in_buffer( excerpt_id, buffer.remote_id(), buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end), ); codegen_ranges.push(anchor_range); if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() { self.telemetry.report_assistant_event(AssistantEvent { conversation_id: None, kind: AssistantKind::Inline, phase: AssistantPhase::Invoked, message_id: None, model: model.telemetry_id(), model_provider: model.provider_id().to_string(), response_latency: None, error_message: None, language_name: buffer.language().map(|language| language.name().to_proto()), }); } } let assist_group_id = self.next_assist_group_id.post_inc(); let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(String::new(), cx)), cx)); let mut assists = Vec::new(); let mut assist_to_focus = None; for range in codegen_ranges { let assist_id = self.next_assist_id.post_inc(); let context_store = cx.new(|_cx| ContextStore::new(workspace.clone())); let codegen = cx.new(|cx| { BufferCodegen::new( editor.read(cx).buffer().clone(), range.clone(), None, context_store.clone(), self.telemetry.clone(), self.prompt_builder.clone(), cx, ) }); let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default())); let prompt_editor = cx.new(|cx| { PromptEditor::new_buffer( assist_id, gutter_dimensions.clone(), self.prompt_history.clone(), prompt_buffer.clone(), codegen.clone(), self.fs.clone(), context_store, workspace.clone(), thread_store.clone(), window, cx, ) }); if assist_to_focus.is_none() { let focus_assist = if newest_selection.reversed { range.start.to_point(&snapshot) == newest_selection.start } else { range.end.to_point(&snapshot) == newest_selection.end }; if focus_assist { assist_to_focus = Some(assist_id); } } let [prompt_block_id, end_block_id] = self.insert_assist_blocks(editor, &range, &prompt_editor, cx); assists.push(( assist_id, range, prompt_editor, prompt_block_id, end_block_id, )); } let editor_assists = self .assists_by_editor .entry(editor.downgrade()) .or_insert_with(|| EditorInlineAssists::new(&editor, window, cx)); let mut assist_group = InlineAssistGroup::new(); for (assist_id, range, prompt_editor, prompt_block_id, end_block_id) in assists { let codegen = prompt_editor.read(cx).codegen().clone(); self.assists.insert( assist_id, InlineAssist::new( assist_id, assist_group_id, editor, &prompt_editor, prompt_block_id, end_block_id, range, codegen, workspace.clone(), window, cx, ), ); assist_group.assist_ids.push(assist_id); editor_assists.assist_ids.push(assist_id); } self.assist_groups.insert(assist_group_id, assist_group); if let Some(assist_id) = assist_to_focus { self.focus_assist(assist_id, window, cx); } } #[allow(clippy::too_many_arguments)] pub fn suggest_assist( &mut self, editor: &Entity<Editor>, mut range: Range<Anchor>, initial_prompt: String, initial_transaction_id: Option<TransactionId>, focus: bool, workspace: WeakEntity<Workspace>, thread_store: Option<WeakEntity<ThreadStore>>, window: &mut Window, cx: &mut App, ) -> InlineAssistId { let assist_group_id = self.next_assist_group_id.post_inc(); let prompt_buffer = cx.new(|cx| Buffer::local(&initial_prompt, cx)); let prompt_buffer = cx.new(|cx| MultiBuffer::singleton(prompt_buffer, cx)); let assist_id = self.next_assist_id.post_inc();