language_model_selector: Don't recreate the Picker
view each render (#21939)
While working on Assistant2, I noticed that the `LanguageModelSelector` was recreating its `Picker` view on every single render. This PR makes it so we create the view once and hold onto it in the parent view. Release Notes: - N/A
This commit is contained in:
parent
d7eba54016
commit
9143fd2924
7 changed files with 263 additions and 240 deletions
|
@ -55,7 +55,7 @@ use language_model::{
|
||||||
LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Role,
|
LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Role,
|
||||||
ZED_CLOUD_PROVIDER_ID,
|
ZED_CLOUD_PROVIDER_ID,
|
||||||
};
|
};
|
||||||
use language_model_selector::{LanguageModelPickerDelegate, LanguageModelSelector};
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use project::lsp_store::LocalLspAdapterDelegate;
|
use project::lsp_store::LocalLspAdapterDelegate;
|
||||||
|
@ -143,7 +143,7 @@ pub struct AssistantPanel {
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
subscriptions: Vec<Subscription>,
|
subscriptions: Vec<Subscription>,
|
||||||
model_selector_menu_handle: PopoverMenuHandle<Picker<LanguageModelPickerDelegate>>,
|
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
model_summary_editor: View<Editor>,
|
model_summary_editor: View<Editor>,
|
||||||
authenticate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,
|
authenticate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,
|
||||||
configuration_subscription: Option<Subscription>,
|
configuration_subscription: Option<Subscription>,
|
||||||
|
@ -341,11 +341,12 @@ impl AssistantPanel {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let model_selector_menu_handle = PopoverMenuHandle::default();
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
let model_summary_editor = cx.new_view(Editor::single_line);
|
let model_summary_editor = cx.new_view(Editor::single_line);
|
||||||
let context_editor_toolbar = cx.new_view(|_| {
|
let context_editor_toolbar = cx.new_view(|cx| {
|
||||||
ContextEditorToolbarItem::new(
|
ContextEditorToolbarItem::new(
|
||||||
workspace,
|
workspace,
|
||||||
model_selector_menu_handle.clone(),
|
model_selector_menu_handle.clone(),
|
||||||
model_summary_editor.clone(),
|
model_summary_editor.clone(),
|
||||||
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4455,23 +4456,36 @@ impl FollowableItem for ContextEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ContextEditorToolbarItem {
|
pub struct ContextEditorToolbarItem {
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
active_context_editor: Option<WeakView<ContextEditor>>,
|
active_context_editor: Option<WeakView<ContextEditor>>,
|
||||||
model_summary_editor: View<Editor>,
|
model_summary_editor: View<Editor>,
|
||||||
model_selector_menu_handle: PopoverMenuHandle<Picker<LanguageModelPickerDelegate>>,
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
|
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextEditorToolbarItem {
|
impl ContextEditorToolbarItem {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
workspace: &Workspace,
|
workspace: &Workspace,
|
||||||
model_selector_menu_handle: PopoverMenuHandle<Picker<LanguageModelPickerDelegate>>,
|
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
model_summary_editor: View<Editor>,
|
model_summary_editor: View<Editor>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fs: workspace.app_state().fs.clone(),
|
|
||||||
active_context_editor: None,
|
active_context_editor: None,
|
||||||
model_summary_editor,
|
model_summary_editor,
|
||||||
model_selector_menu_handle,
|
language_model_selector: cx.new_view(|cx| {
|
||||||
|
let fs = workspace.app_state().fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
language_model_selector_menu_handle: model_selector_menu_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4560,17 +4574,8 @@ impl Render for ContextEditorToolbarItem {
|
||||||
// .map(|remaining_items| format!("Files to scan: {}", remaining_items))
|
// .map(|remaining_items| format!("Files to scan: {}", remaining_items))
|
||||||
// })
|
// })
|
||||||
.child(
|
.child(
|
||||||
LanguageModelSelector::new(
|
LanguageModelSelectorPopoverMenu::new(
|
||||||
{
|
self.language_model_selector.clone(),
|
||||||
let fs = self.fs.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ButtonLike::new("active-model")
|
ButtonLike::new("active-model")
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.child(
|
.child(
|
||||||
|
@ -4616,7 +4621,7 @@ impl Render for ContextEditorToolbarItem {
|
||||||
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
|
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.with_handle(self.model_selector_menu_handle.clone()),
|
.with_handle(self.language_model_selector_menu_handle.clone()),
|
||||||
)
|
)
|
||||||
.children(self.render_remaining_tokens(cx));
|
.children(self.render_remaining_tokens(cx));
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ use language_model::{
|
||||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||||
LanguageModelTextStream, Role,
|
LanguageModelTextStream, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use language_models::report_assistant_event;
|
use language_models::report_assistant_event;
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -1358,8 +1358,8 @@ enum PromptEditorEvent {
|
||||||
|
|
||||||
struct PromptEditor {
|
struct PromptEditor {
|
||||||
id: InlineAssistId,
|
id: InlineAssistId,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
|
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
|
@ -1500,18 +1500,8 @@ impl Render for PromptEditor {
|
||||||
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
LanguageModelSelector::new(
|
self.language_model_selector.clone(),
|
||||||
{
|
|
||||||
let fs = self.fs.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IconButton::new("context", IconName::SettingsAlt)
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
.shape(IconButtonShape::Square)
|
.shape(IconButtonShape::Square)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -1530,13 +1520,7 @@ impl Render for PromptEditor {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
))
|
||||||
.info_text(
|
|
||||||
"Inline edits use context\n\
|
|
||||||
from the currently selected\n\
|
|
||||||
assistant panel tab.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map(|el| {
|
.map(|el| {
|
||||||
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
||||||
return el;
|
return el;
|
||||||
|
@ -1642,6 +1626,19 @@ impl PromptEditor {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
id,
|
id,
|
||||||
editor: prompt_editor,
|
editor: prompt_editor,
|
||||||
|
language_model_selector: cx.new_view(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
gutter_dimensions,
|
gutter_dimensions,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
@ -1650,7 +1647,6 @@ impl PromptEditor {
|
||||||
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
||||||
editor_subscriptions: Vec::new(),
|
editor_subscriptions: Vec::new(),
|
||||||
codegen,
|
codegen,
|
||||||
fs,
|
|
||||||
pending_token_count: Task::ready(Ok(())),
|
pending_token_count: Task::ready(Ok(())),
|
||||||
token_counts: None,
|
token_counts: None,
|
||||||
_token_count_subscriptions: token_count_subscriptions,
|
_token_count_subscriptions: token_count_subscriptions,
|
||||||
|
|
|
@ -20,7 +20,7 @@ use language::Buffer;
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use language_models::report_assistant_event;
|
use language_models::report_assistant_event;
|
||||||
use settings::{update_settings_file, Settings};
|
use settings::{update_settings_file, Settings};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -476,9 +476,9 @@ enum PromptEditorEvent {
|
||||||
|
|
||||||
struct PromptEditor {
|
struct PromptEditor {
|
||||||
id: TerminalInlineAssistId,
|
id: TerminalInlineAssistId,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
height_in_lines: u8,
|
height_in_lines: u8,
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
prompt_history_ix: Option<usize>,
|
prompt_history_ix: Option<usize>,
|
||||||
|
@ -614,17 +614,8 @@ impl Render for PromptEditor {
|
||||||
.w_12()
|
.w_12()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(LanguageModelSelector::new(
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
{
|
self.language_model_selector.clone(),
|
||||||
let fs = self.fs.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IconButton::new("context", IconName::SettingsAlt)
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
.shape(IconButtonShape::Square)
|
.shape(IconButtonShape::Square)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -718,6 +709,19 @@ impl PromptEditor {
|
||||||
id,
|
id,
|
||||||
height_in_lines: 1,
|
height_in_lines: 1,
|
||||||
editor: prompt_editor,
|
editor: prompt_editor,
|
||||||
|
language_model_selector: cx.new_view(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
prompt_history_ix: None,
|
prompt_history_ix: None,
|
||||||
|
@ -725,7 +729,6 @@ impl PromptEditor {
|
||||||
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
||||||
editor_subscriptions: Vec::new(),
|
editor_subscriptions: Vec::new(),
|
||||||
codegen,
|
codegen,
|
||||||
fs,
|
|
||||||
pending_token_count: Task::ready(Ok(())),
|
pending_token_count: Task::ready(Ok(())),
|
||||||
token_count: None,
|
token_count: None,
|
||||||
_token_count_subscriptions: token_count_subscriptions,
|
_token_count_subscriptions: token_count_subscriptions,
|
||||||
|
|
|
@ -31,7 +31,7 @@ use language_model::{
|
||||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||||
LanguageModelTextStream, Role,
|
LanguageModelTextStream, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use language_models::report_assistant_event;
|
use language_models::report_assistant_event;
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -1454,8 +1454,8 @@ enum PromptEditorEvent {
|
||||||
|
|
||||||
struct PromptEditor {
|
struct PromptEditor {
|
||||||
id: InlineAssistId,
|
id: InlineAssistId,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
|
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
|
@ -1589,18 +1589,8 @@ impl Render for PromptEditor {
|
||||||
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
LanguageModelSelector::new(
|
self.language_model_selector.clone(),
|
||||||
{
|
|
||||||
let fs = self.fs.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IconButton::new("context", IconName::SettingsAlt)
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
.shape(IconButtonShape::Square)
|
.shape(IconButtonShape::Square)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -1619,13 +1609,7 @@ impl Render for PromptEditor {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
))
|
||||||
.info_text(
|
|
||||||
"Inline edits use context\n\
|
|
||||||
from the currently selected\n\
|
|
||||||
assistant panel tab.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map(|el| {
|
.map(|el| {
|
||||||
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
||||||
return el;
|
return el;
|
||||||
|
@ -1714,6 +1698,19 @@ impl PromptEditor {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
id,
|
id,
|
||||||
editor: prompt_editor,
|
editor: prompt_editor,
|
||||||
|
language_model_selector: cx.new_view(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
gutter_dimensions,
|
gutter_dimensions,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
@ -1722,7 +1719,6 @@ impl PromptEditor {
|
||||||
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
||||||
editor_subscriptions: Vec::new(),
|
editor_subscriptions: Vec::new(),
|
||||||
codegen,
|
codegen,
|
||||||
fs,
|
|
||||||
show_rate_limit_notice: false,
|
show_rate_limit_notice: false,
|
||||||
};
|
};
|
||||||
this.subscribe_to_editor(cx);
|
this.subscribe_to_editor(cx);
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::rc::Rc;
|
||||||
use editor::{Editor, EditorElement, EditorStyle};
|
use editor::{Editor, EditorElement, EditorStyle};
|
||||||
use gpui::{AppContext, FocusableView, Model, TextStyle, View, WeakView};
|
use gpui::{AppContext, FocusableView, Model, TextStyle, View, WeakView};
|
||||||
use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
|
use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{
|
use ui::{
|
||||||
|
@ -25,6 +25,7 @@ pub struct MessageEditor {
|
||||||
next_context_id: ContextId,
|
next_context_id: ContextId,
|
||||||
context_picker: View<ContextPicker>,
|
context_picker: View<ContextPicker>,
|
||||||
pub(crate) context_picker_handle: PopoverMenuHandle<ContextPicker>,
|
pub(crate) context_picker_handle: PopoverMenuHandle<ContextPicker>,
|
||||||
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
use_tools: bool,
|
use_tools: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +48,14 @@ impl MessageEditor {
|
||||||
next_context_id: ContextId(0),
|
next_context_id: ContextId(0),
|
||||||
context_picker: cx.new_view(|cx| ContextPicker::new(workspace.clone(), weak_self, cx)),
|
context_picker: cx.new_view(|cx| ContextPicker::new(workspace.clone(), weak_self, cx)),
|
||||||
context_picker_handle: PopoverMenuHandle::default(),
|
context_picker_handle: PopoverMenuHandle::default(),
|
||||||
|
language_model_selector: cx.new_view(|cx| {
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
|model, _cx| {
|
||||||
|
println!("Selected {:?}", model.name());
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
use_tools: false,
|
use_tools: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,10 +129,8 @@ impl MessageEditor {
|
||||||
let active_provider = LanguageModelRegistry::read_global(cx).active_provider();
|
let active_provider = LanguageModelRegistry::read_global(cx).active_provider();
|
||||||
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||||
|
|
||||||
LanguageModelSelector::new(
|
LanguageModelSelectorPopoverMenu::new(
|
||||||
|model, _cx| {
|
self.language_model_selector.clone(),
|
||||||
println!("Selected {:?}", model.name());
|
|
||||||
},
|
|
||||||
ButtonLike::new("active-model")
|
ButtonLike::new("active-model")
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.child(
|
.child(
|
||||||
|
|
|
@ -17,7 +17,7 @@ use language::Buffer;
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use language_models::report_assistant_event;
|
use language_models::report_assistant_event;
|
||||||
use settings::{update_settings_file, Settings};
|
use settings::{update_settings_file, Settings};
|
||||||
use std::{cmp, sync::Arc, time::Instant};
|
use std::{cmp, sync::Arc, time::Instant};
|
||||||
|
@ -439,9 +439,9 @@ enum PromptEditorEvent {
|
||||||
|
|
||||||
struct PromptEditor {
|
struct PromptEditor {
|
||||||
id: TerminalInlineAssistId,
|
id: TerminalInlineAssistId,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
height_in_lines: u8,
|
height_in_lines: u8,
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
prompt_history_ix: Option<usize>,
|
prompt_history_ix: Option<usize>,
|
||||||
|
@ -575,17 +575,8 @@ impl Render for PromptEditor {
|
||||||
.w_12()
|
.w_12()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(LanguageModelSelector::new(
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
{
|
self.language_model_selector.clone(),
|
||||||
let fs = self.fs.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IconButton::new("context", IconName::SettingsAlt)
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
.shape(IconButtonShape::Square)
|
.shape(IconButtonShape::Square)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -665,6 +656,19 @@ impl PromptEditor {
|
||||||
id,
|
id,
|
||||||
height_in_lines: 1,
|
height_in_lines: 1,
|
||||||
editor: prompt_editor,
|
editor: prompt_editor,
|
||||||
|
language_model_selector: cx.new_view(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
prompt_history_ix: None,
|
prompt_history_ix: None,
|
||||||
|
@ -672,7 +676,6 @@ impl PromptEditor {
|
||||||
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
|
||||||
editor_subscriptions: Vec::new(),
|
editor_subscriptions: Vec::new(),
|
||||||
codegen,
|
codegen,
|
||||||
fs,
|
|
||||||
};
|
};
|
||||||
this.count_lines(cx);
|
this.count_lines(cx);
|
||||||
this.subscribe_to_editor(cx);
|
this.subscribe_to_editor(cx);
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use feature_flags::ZedPro;
|
use feature_flags::ZedPro;
|
||||||
use gpui::{Action, AnyElement, AppContext, DismissEvent, SharedString, Task};
|
use gpui::{
|
||||||
|
Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Task,
|
||||||
|
View, WeakView,
|
||||||
|
};
|
||||||
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
|
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use proto::Plan;
|
use proto::Plan;
|
||||||
|
@ -12,19 +15,101 @@ const TRY_ZED_PRO_URL: &str = "https://zed.dev/pro";
|
||||||
|
|
||||||
type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &AppContext) + 'static>;
|
type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &AppContext) + 'static>;
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
pub struct LanguageModelSelector {
|
||||||
pub struct LanguageModelSelector<T: PopoverTrigger> {
|
picker: View<Picker<LanguageModelPickerDelegate>>,
|
||||||
handle: Option<PopoverMenuHandle<Picker<LanguageModelPickerDelegate>>>,
|
|
||||||
on_model_changed: OnModelChanged,
|
|
||||||
trigger: T,
|
|
||||||
info_text: Option<SharedString>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LanguageModelPickerDelegate {
|
impl LanguageModelSelector {
|
||||||
on_model_changed: OnModelChanged,
|
pub fn new(
|
||||||
all_models: Vec<ModelInfo>,
|
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &AppContext) + 'static,
|
||||||
filtered_models: Vec<ModelInfo>,
|
cx: &mut ViewContext<Self>,
|
||||||
selected_index: usize,
|
) -> Self {
|
||||||
|
let on_model_changed = Arc::new(on_model_changed);
|
||||||
|
|
||||||
|
let all_models = LanguageModelRegistry::global(cx)
|
||||||
|
.read(cx)
|
||||||
|
.providers()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|provider| {
|
||||||
|
let icon = provider.icon();
|
||||||
|
|
||||||
|
provider.provided_models(cx).into_iter().map(move |model| {
|
||||||
|
let model = model.clone();
|
||||||
|
let icon = model.icon().unwrap_or(icon);
|
||||||
|
|
||||||
|
ModelInfo {
|
||||||
|
model: model.clone(),
|
||||||
|
icon,
|
||||||
|
availability: model.availability(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let delegate = LanguageModelPickerDelegate {
|
||||||
|
language_model_selector: cx.view().downgrade(),
|
||||||
|
on_model_changed: on_model_changed.clone(),
|
||||||
|
all_models: all_models.clone(),
|
||||||
|
filtered_models: all_models,
|
||||||
|
selected_index: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let picker =
|
||||||
|
cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
|
||||||
|
|
||||||
|
LanguageModelSelector { picker }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<DismissEvent> for LanguageModelSelector {}
|
||||||
|
|
||||||
|
impl FocusableView for LanguageModelSelector {
|
||||||
|
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||||
|
self.picker.focus_handle(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for LanguageModelSelector {
|
||||||
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
self.picker.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct LanguageModelSelectorPopoverMenu<T>
|
||||||
|
where
|
||||||
|
T: PopoverTrigger,
|
||||||
|
{
|
||||||
|
language_model_selector: View<LanguageModelSelector>,
|
||||||
|
trigger: T,
|
||||||
|
handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PopoverTrigger> LanguageModelSelectorPopoverMenu<T> {
|
||||||
|
pub fn new(language_model_selector: View<LanguageModelSelector>, trigger: T) -> Self {
|
||||||
|
Self {
|
||||||
|
language_model_selector,
|
||||||
|
trigger,
|
||||||
|
handle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_handle(mut self, handle: PopoverMenuHandle<LanguageModelSelector>) -> Self {
|
||||||
|
self.handle = Some(handle);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PopoverTrigger> RenderOnce for LanguageModelSelectorPopoverMenu<T> {
|
||||||
|
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let language_model_selector = self.language_model_selector.clone();
|
||||||
|
|
||||||
|
PopoverMenu::new("model-switcher")
|
||||||
|
.menu(move |_cx| Some(language_model_selector.clone()))
|
||||||
|
.trigger(self.trigger)
|
||||||
|
.attach(gpui::AnchorCorner::BottomLeft)
|
||||||
|
.when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -32,34 +117,14 @@ struct ModelInfo {
|
||||||
model: Arc<dyn LanguageModel>,
|
model: Arc<dyn LanguageModel>,
|
||||||
icon: IconName,
|
icon: IconName,
|
||||||
availability: LanguageModelAvailability,
|
availability: LanguageModelAvailability,
|
||||||
is_selected: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PopoverTrigger> LanguageModelSelector<T> {
|
pub struct LanguageModelPickerDelegate {
|
||||||
pub fn new(
|
language_model_selector: WeakView<LanguageModelSelector>,
|
||||||
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &AppContext) + 'static,
|
on_model_changed: OnModelChanged,
|
||||||
trigger: T,
|
all_models: Vec<ModelInfo>,
|
||||||
) -> Self {
|
filtered_models: Vec<ModelInfo>,
|
||||||
LanguageModelSelector {
|
selected_index: usize,
|
||||||
handle: None,
|
|
||||||
on_model_changed: Arc::new(on_model_changed),
|
|
||||||
trigger,
|
|
||||||
info_text: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_handle(
|
|
||||||
mut self,
|
|
||||||
handle: PopoverMenuHandle<Picker<LanguageModelPickerDelegate>>,
|
|
||||||
) -> Self {
|
|
||||||
self.handle = Some(handle);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn info_text(mut self, text: impl Into<SharedString>) -> Self {
|
|
||||||
self.info_text = Some(text.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerDelegate for LanguageModelPickerDelegate {
|
impl PickerDelegate for LanguageModelPickerDelegate {
|
||||||
|
@ -142,23 +207,15 @@ impl PickerDelegate for LanguageModelPickerDelegate {
|
||||||
let model = model_info.model.clone();
|
let model = model_info.model.clone();
|
||||||
(self.on_model_changed)(model.clone(), cx);
|
(self.on_model_changed)(model.clone(), cx);
|
||||||
|
|
||||||
// Update the selection status
|
|
||||||
let selected_model_id = model_info.model.id();
|
|
||||||
let selected_provider_id = model_info.model.provider_id();
|
|
||||||
for model in &mut self.all_models {
|
|
||||||
model.is_selected = model.model.id() == selected_model_id
|
|
||||||
&& model.model.provider_id() == selected_provider_id;
|
|
||||||
}
|
|
||||||
for model in &mut self.filtered_models {
|
|
||||||
model.is_selected = model.model.id() == selected_model_id
|
|
||||||
&& model.model.provider_id() == selected_provider_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
self.language_model_selector
|
||||||
|
.update(cx, |_this, cx| cx.emit(DismissEvent))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
fn render_header(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
|
fn render_header(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
|
||||||
let configured_models_count = LanguageModelRegistry::global(cx)
|
let configured_models_count = LanguageModelRegistry::global(cx)
|
||||||
|
@ -195,6 +252,17 @@ impl PickerDelegate for LanguageModelPickerDelegate {
|
||||||
let model_info = self.filtered_models.get(ix)?;
|
let model_info = self.filtered_models.get(ix)?;
|
||||||
let provider_name: String = model_info.model.provider_name().0.clone().into();
|
let provider_name: String = model_info.model.provider_name().0.clone().into();
|
||||||
|
|
||||||
|
let active_provider_id = LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_provider()
|
||||||
|
.map(|m| m.id());
|
||||||
|
|
||||||
|
let active_model_id = LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_model()
|
||||||
|
.map(|m| m.id());
|
||||||
|
|
||||||
|
let is_selected = Some(model_info.model.provider_id()) == active_provider_id
|
||||||
|
&& Some(model_info.model.id()) == active_model_id;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
ListItem::new(ix)
|
ListItem::new(ix)
|
||||||
.inset(true)
|
.inset(true)
|
||||||
|
@ -235,7 +303,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.end_slot(div().when(model_info.is_selected, |this| {
|
.end_slot(div().when(is_selected, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
Icon::new(IconName::Check)
|
Icon::new(IconName::Check)
|
||||||
.color(Color::Accent)
|
.color(Color::Accent)
|
||||||
|
@ -296,58 +364,3 @@ impl PickerDelegate for LanguageModelPickerDelegate {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PopoverTrigger> RenderOnce for LanguageModelSelector<T> {
|
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
let selected_provider = LanguageModelRegistry::read_global(cx)
|
|
||||||
.active_provider()
|
|
||||||
.map(|m| m.id());
|
|
||||||
|
|
||||||
let selected_model = LanguageModelRegistry::read_global(cx)
|
|
||||||
.active_model()
|
|
||||||
.map(|m| m.id());
|
|
||||||
|
|
||||||
let all_models = LanguageModelRegistry::global(cx)
|
|
||||||
.read(cx)
|
|
||||||
.providers()
|
|
||||||
.iter()
|
|
||||||
.flat_map(|provider| {
|
|
||||||
let provider_id = provider.id();
|
|
||||||
let icon = provider.icon();
|
|
||||||
let selected_model = selected_model.clone();
|
|
||||||
let selected_provider = selected_provider.clone();
|
|
||||||
|
|
||||||
provider.provided_models(cx).into_iter().map(move |model| {
|
|
||||||
let model = model.clone();
|
|
||||||
let icon = model.icon().unwrap_or(icon);
|
|
||||||
|
|
||||||
ModelInfo {
|
|
||||||
model: model.clone(),
|
|
||||||
icon,
|
|
||||||
availability: model.availability(),
|
|
||||||
is_selected: selected_model.as_ref() == Some(&model.id())
|
|
||||||
&& selected_provider.as_ref() == Some(&provider_id),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let delegate = LanguageModelPickerDelegate {
|
|
||||||
on_model_changed: self.on_model_changed.clone(),
|
|
||||||
all_models: all_models.clone(),
|
|
||||||
filtered_models: all_models,
|
|
||||||
selected_index: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let picker_view = cx.new_view(|cx| {
|
|
||||||
let picker = Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into()));
|
|
||||||
picker
|
|
||||||
});
|
|
||||||
|
|
||||||
PopoverMenu::new("model-switcher")
|
|
||||||
.menu(move |_cx| Some(picker_view.clone()))
|
|
||||||
.trigger(self.trigger)
|
|
||||||
.attach(gpui::AnchorCorner::BottomLeft)
|
|
||||||
.when_some(self.handle, |menu, handle| menu.with_handle(handle))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue