mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
sidebar: Debounce Sidebar::update_entries
This commit is contained in:
parent
f839f8e108
commit
a1907cc7cf
6 changed files with 69 additions and 29 deletions
|
|
@ -74,9 +74,10 @@ pub use crate::agent_panel::{
|
|||
};
|
||||
use crate::agent_registry_ui::AgentRegistryPage;
|
||||
pub use crate::inline_assistant::InlineAssistant;
|
||||
pub use crate::message_editor::MessageEditorEvent;
|
||||
pub use crate::thread_metadata_store::ThreadId;
|
||||
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
|
||||
pub use conversation_view::ConversationView;
|
||||
pub use conversation_view::{ConversationView, StateChange};
|
||||
pub use external_source_prompt::ExternalSourcePrompt;
|
||||
pub(crate) use mode_selector::ModeSelector;
|
||||
pub(crate) use model_selector::ModelSelector;
|
||||
|
|
|
|||
|
|
@ -456,6 +456,10 @@ pub(crate) struct RootThreadUpdated;
|
|||
|
||||
impl EventEmitter<RootThreadUpdated> for ConversationView {}
|
||||
|
||||
pub struct StateChange;
|
||||
|
||||
impl EventEmitter<StateChange> for ConversationView {}
|
||||
|
||||
fn resolve_outcome_from_selection(
|
||||
options: &PermissionOptions,
|
||||
selection: Option<&thread_view::PermissionSelection>,
|
||||
|
|
@ -829,6 +833,7 @@ impl ConversationView {
|
|||
}
|
||||
|
||||
self.server_state = state;
|
||||
cx.emit(StateChange);
|
||||
cx.emit(AcpServerViewEvent::ActiveThreadChanged);
|
||||
if matches!(&self.server_state, ServerState::Connected(_)) {
|
||||
cx.emit(RootThreadUpdated);
|
||||
|
|
@ -1338,6 +1343,7 @@ impl ConversationView {
|
|||
};
|
||||
if let Some(connected) = this.as_connected_mut() {
|
||||
connected.auth_state = auth_state;
|
||||
cx.emit(StateChange);
|
||||
if let Some(view) = connected.active_view()
|
||||
&& view
|
||||
.read(cx)
|
||||
|
|
@ -1832,6 +1838,7 @@ impl ConversationView {
|
|||
pending_auth_method.replace(method.clone());
|
||||
|
||||
let project = self.project.clone();
|
||||
cx.emit(StateChange);
|
||||
cx.notify();
|
||||
self.auth_task = Some(cx.spawn_in(window, {
|
||||
async move |this, cx| {
|
||||
|
|
@ -1877,6 +1884,7 @@ impl ConversationView {
|
|||
}) = this.as_connected_mut()
|
||||
{
|
||||
pending_auth_method.take();
|
||||
cx.emit(StateChange);
|
||||
}
|
||||
if let Some(active) = this.root_thread_view() {
|
||||
active.update(cx, |active, cx| {
|
||||
|
|
@ -1898,6 +1906,7 @@ impl ConversationView {
|
|||
pending_auth_method.replace(method.clone());
|
||||
|
||||
let authenticate = connection.authenticate(method, cx);
|
||||
cx.emit(StateChange);
|
||||
cx.notify();
|
||||
self.auth_task = Some(cx.spawn_in(window, {
|
||||
async move |this, cx| {
|
||||
|
|
@ -1925,6 +1934,7 @@ impl ConversationView {
|
|||
}) = this.as_connected_mut()
|
||||
{
|
||||
pending_auth_method.take();
|
||||
cx.emit(StateChange);
|
||||
}
|
||||
if let Some(active) = this.root_thread_view() {
|
||||
active.update(cx, |active, cx| active.handle_thread_error(err, cx));
|
||||
|
|
@ -3009,6 +3019,7 @@ impl ConversationView {
|
|||
pending_auth_method: None,
|
||||
_subscription: None,
|
||||
};
|
||||
cx.emit(StateChange);
|
||||
if let Some(view) = connected.active_view()
|
||||
&& view
|
||||
.read(cx)
|
||||
|
|
|
|||
|
|
@ -1002,6 +1002,7 @@ impl ThreadView {
|
|||
MessageEditorEvent::LostFocus => {}
|
||||
MessageEditorEvent::SlashAutocompleteOpened => {}
|
||||
MessageEditorEvent::InputAttempted { .. } => {}
|
||||
MessageEditorEvent::Edited => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1142,6 +1143,7 @@ impl ThreadView {
|
|||
}
|
||||
ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::SlashAutocompleteOpened) => {
|
||||
}
|
||||
ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::Edited) => {}
|
||||
ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::InputAttempted { .. }) => {}
|
||||
ViewEvent::OpenDiffLocation {
|
||||
path,
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ pub enum MessageEditorEvent {
|
|||
Cancel,
|
||||
Focus,
|
||||
LostFocus,
|
||||
Edited,
|
||||
/// Emitted when the user opens slash-command autocomplete in this
|
||||
/// editor. Used by `ThreadView` to fire the global-skills scan
|
||||
/// trigger; see `NativeAgent::ensure_skills_scan_started`.
|
||||
|
|
@ -559,6 +560,7 @@ impl MessageEditor {
|
|||
if let EditorEvent::Edited { .. } = event
|
||||
&& !editor.read(cx).read_only(cx)
|
||||
{
|
||||
cx.emit(MessageEditorEvent::Edited);
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
this.mention_set
|
||||
|
|
|
|||
|
|
@ -11474,8 +11474,11 @@ pub enum EditorEvent {
|
|||
RestoreRequested {
|
||||
hunks: Vec<MultiBufferDiffHunk>,
|
||||
},
|
||||
/// Emitted when an underlying buffer changes, including edits made through another editor.
|
||||
BufferEdited,
|
||||
/// Emitted when this editor creates, undoes, or redoes an edit transaction.
|
||||
Edited {
|
||||
/// The transaction that changed the editor's buffer.
|
||||
transaction_id: clock::Lamport,
|
||||
},
|
||||
Reparsed(BufferId),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use agent_ui::terminal_thread_metadata_store::{
|
|||
use agent_ui::thread_metadata_store::{
|
||||
ThreadMetadata, ThreadMetadataStore, WorktreePaths, worktree_info_from_thread_paths,
|
||||
};
|
||||
use agent_ui::thread_worktree_archive;
|
||||
use agent_ui::threads_archive_view::{
|
||||
ThreadsArchiveView, ThreadsArchiveViewEvent, format_history_entry_timestamp,
|
||||
fuzzy_match_positions,
|
||||
|
|
@ -21,6 +20,7 @@ use agent_ui::{
|
|||
NewThread, RenameSelectedThread, TerminalId, ThreadId, ThreadImportModal,
|
||||
channels_with_threads, import_threads_from_other_channels,
|
||||
};
|
||||
use agent_ui::{MessageEditorEvent, StateChange, thread_worktree_archive};
|
||||
use chrono::{DateTime, Utc};
|
||||
use editor::Editor;
|
||||
use feature_flags::{
|
||||
|
|
@ -686,6 +686,7 @@ pub struct Sidebar {
|
|||
project_header_menu_ix: Option<usize>,
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
_draft_editor_observations: Vec<gpui::Subscription>,
|
||||
update_task: Option<Task<()>>,
|
||||
/// For the thread import banners, if there is just one we show "Import
|
||||
/// Threads" but if we are showing both the external agents and other
|
||||
/// channels import banners then we change the text to disambiguate the
|
||||
|
|
@ -720,15 +721,15 @@ impl Sidebar {
|
|||
MultiWorkspaceEvent::ActiveWorkspaceChanged { .. } => {
|
||||
this.sync_active_entry_from_active_workspace(cx);
|
||||
this.replace_archived_panel_thread(window, cx);
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
MultiWorkspaceEvent::WorkspaceAdded(workspace) => {
|
||||
this.subscribe_to_workspace(workspace, window, cx);
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
MultiWorkspaceEvent::WorkspaceRemoved(_)
|
||||
| MultiWorkspaceEvent::ProjectGroupsChanged => {
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -740,10 +741,7 @@ impl Sidebar {
|
|||
if !query.is_empty() {
|
||||
this.selection.take();
|
||||
}
|
||||
this.update_entries(cx);
|
||||
if !query.is_empty() {
|
||||
this.select_first_entry();
|
||||
}
|
||||
this.schedule_update_entries(!query.is_empty(), cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
|
@ -758,14 +756,14 @@ impl Sidebar {
|
|||
.detach();
|
||||
|
||||
cx.observe(&ThreadMetadataStore::global(cx), |this, _store, cx| {
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.observe(
|
||||
&TerminalThreadMetadataStore::global(cx),
|
||||
|this, _store, cx| {
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
|
@ -778,7 +776,7 @@ impl Sidebar {
|
|||
this.subscribe_to_workspace(workspace, window, cx);
|
||||
}
|
||||
}
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
});
|
||||
|
||||
Self {
|
||||
|
|
@ -808,6 +806,7 @@ impl Sidebar {
|
|||
project_header_menu_ix: None,
|
||||
_subscriptions: Vec::new(),
|
||||
_draft_editor_observations: Vec::new(),
|
||||
update_task: None,
|
||||
import_banners_use_verbose_labels: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -862,11 +861,11 @@ impl Sidebar {
|
|||
ProjectEvent::WorktreeAdded(_)
|
||||
| ProjectEvent::WorktreeRemoved(_)
|
||||
| ProjectEvent::WorktreeOrderChanged => {
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
ProjectEvent::WorktreePathsChanged { old_worktree_paths } => {
|
||||
this.move_entry_paths(project, old_worktree_paths, cx);
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
|
@ -887,7 +886,7 @@ impl Sidebar {
|
|||
_,
|
||||
)
|
||||
) {
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -900,7 +899,7 @@ impl Sidebar {
|
|||
if let workspace::Event::PanelAdded(view) = event {
|
||||
if let Ok(agent_panel) = view.clone().downcast::<AgentPanel>() {
|
||||
this.subscribe_to_agent_panel(workspace, &agent_panel, window, cx);
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -992,7 +991,7 @@ impl Sidebar {
|
|||
| AgentPanelEvent::ActiveViewFocused
|
||||
| AgentPanelEvent::EntryChanged => {
|
||||
this.sync_active_entry_from_panel(agent_panel, cx);
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
AgentPanelEvent::TerminalClosed { metadata } => {
|
||||
if let Some(workspace) = workspace.upgrade() {
|
||||
|
|
@ -1002,7 +1001,7 @@ impl Sidebar {
|
|||
}
|
||||
AgentPanelEvent::ThreadInteracted { thread_id } => {
|
||||
this.record_thread_interacted(thread_id, cx);
|
||||
this.update_entries(cx);
|
||||
this.schedule_update_entries(false, cx);
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -1858,6 +1857,24 @@ impl Sidebar {
|
|||
};
|
||||
}
|
||||
|
||||
fn schedule_update_entries(&mut self, select_first_after_update: bool, cx: &mut Context<Self>) {
|
||||
if self.update_task.is_some() && !select_first_after_update {
|
||||
return;
|
||||
}
|
||||
|
||||
self.update_task = Some(cx.spawn(async move |this, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.update_task = None;
|
||||
this.update_entries(cx);
|
||||
if select_first_after_update {
|
||||
this.select_first_entry();
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}));
|
||||
}
|
||||
|
||||
/// Rebuilds the sidebar's visible entries from already-cached state.
|
||||
fn update_entries(&mut self, cx: &mut Context<Self>) {
|
||||
let Some(multi_workspace) = self.multi_workspace.upgrade() else {
|
||||
|
|
@ -1939,8 +1956,8 @@ impl Sidebar {
|
|||
/// Re-establishes subscriptions to each visible draft's message editor
|
||||
/// so we rebuild entries (and their displayed titles) as the user types.
|
||||
fn refresh_draft_editor_observations(&mut self, cx: &mut Context<Self>) {
|
||||
let Some(multi_workspace) = self.multi_workspace.upgrade() else {
|
||||
self._draft_editor_observations.clear();
|
||||
let Some(multi_workspace) = self.multi_workspace.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
@ -1951,23 +1968,27 @@ impl Sidebar {
|
|||
.flat_map(|panel| panel.read(cx).conversation_views())
|
||||
.collect();
|
||||
|
||||
let mut subscriptions = Vec::with_capacity(draft_conversation_views.len());
|
||||
for cv in draft_conversation_views {
|
||||
if let Some(thread_view) = cv.read(cx).active_thread() {
|
||||
let editor = thread_view.read(cx).message_editor.clone();
|
||||
subscriptions.push(cx.observe(&editor, |this, _editor, cx| {
|
||||
this.update_entries(cx);
|
||||
}));
|
||||
self._draft_editor_observations.push(cx.subscribe(
|
||||
&editor,
|
||||
|this, _editor, event, cx| match event {
|
||||
MessageEditorEvent::Edited => this.schedule_update_entries(false, cx),
|
||||
_ => (),
|
||||
},
|
||||
));
|
||||
}
|
||||
// Also observe the ConversationView itself so that editor
|
||||
// Also subscribe to the ConversationView itself so that editor
|
||||
// replacements during lifecycle transitions (Loading →
|
||||
// Connected) re-wire the editor observation above.
|
||||
subscriptions.push(cx.observe(&cv, |this, _cv, cx| {
|
||||
this.refresh_draft_editor_observations(cx);
|
||||
this.update_entries(cx);
|
||||
}));
|
||||
self._draft_editor_observations.push(cx.subscribe(
|
||||
&cv,
|
||||
|this, _cv, _event: &StateChange, cx| {
|
||||
this.schedule_update_entries(false, cx);
|
||||
},
|
||||
));
|
||||
}
|
||||
self._draft_editor_observations = subscriptions;
|
||||
}
|
||||
|
||||
fn select_first_entry(&mut self) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue