use crate::session::running::{RunningState, memory_view::MemoryView}; use super::stack_frame_list::{StackFrameList, StackFrameListEvent};use dap::{ ScopePresentationHint, StackFrameId, VariablePresentationHint, VariablePresentationHintKind, VariableReference,};use editor::Editor;use gpui::{ Action, AnyElement, ClickEvent, ClipboardItem, Context, DismissEvent, Empty, Entity, FocusHandle, Focusable, Hsla, MouseDownEvent, Point, Subscription, TextStyleRefinement, UniformListScrollHandle, WeakEntity, actions, anchored, deferred, uniform_list,};use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious};use project::debugger::{ dap_command::DataBreakpointContext, session::{Session, SessionEvent, Watcher},};use std::{collections::HashMap, ops::Range, sync::Arc};use ui::{ContextMenu, ListItem, ScrollableHandle, Tooltip, WithScrollbar, prelude::*};use util::{debug_panic, maybe}; actions!( variable_list, [ /// Expands the selected variable entry to show its children. ExpandSelectedEntry, /// Collapses the selected variable entry to hide its children. CollapseSelectedEntry, /// Copies the variable name to the clipboard. CopyVariableName, /// Copies the variable value to the clipboard. CopyVariableValue, /// Edits the value of the selected variable. EditVariable, /// Adds the selected variable to the watch list. AddWatch, /// Removes the selected variable from the watch list. RemoveWatch, /// Jump to variable's memory location. GoToMemory, ]); #[derive(Debug, Copy, Clone, PartialEq, Eq)]pub(crate) struct EntryState { depth: usize, is_expanded: bool, has_children: bool, parent_reference: VariableReference,} #[derive(Debug, PartialEq, Eq, Hash, Clone)]pub(crate) struct EntryPath { pub leaf_name: Option<SharedString>, pub indices: Arc<[SharedString]>,} impl EntryPath { fn for_watcher(expression: impl Into<SharedString>) -> Self { Self { leaf_name: Some(expression.into()), indices: Arc::new([]), } } fn for_scope(scope_name: impl Into<SharedString>) -> Self { Self { leaf_name: Some(scope_name.into()), indices: Arc::new([]), } } fn with_name(&self, name: SharedString) -> Self { Self { leaf_name: Some(name), indices: self.indices.clone(), } } /// Create a new child of this variable path fn with_child(&self, name: SharedString) -> Self { Self { leaf_name: None, indices: self .indices .iter() .cloned() .chain(std::iter::once(name)) .collect(), } }} #[derive(Debug, Clone, PartialEq)]enum DapEntry { Watcher(Watcher), Variable(dap::Variable), Scope(dap::Scope),} impl DapEntry { fn as_watcher(&self) -> Option<&Watcher> { match self { DapEntry::Watcher(watcher) => Some(watcher), _ => None, } } fn as_variable(&self) -> Option<&dap::Variable> { match self { DapEntry::Variable(dap) => Some(dap), _ => None, } } fn as_scope(&self) -> Option<&dap::Scope> { match self { DapEntry::Scope(dap) => Some(dap), _ => None, } } #[cfg(test)] fn name(&self) -> &str { match self { DapEntry::Watcher(watcher) => &watcher.expression, DapEntry::Variable(dap) => &dap.name, DapEntry::Scope(dap) => &dap.name, } }} #[derive(Debug, Clone, PartialEq)]struct ListEntry { entry: DapEntry, path: EntryPath,} impl ListEntry { fn as_watcher(&self) -> Option<&Watcher> { self.entry.as_watcher() } fn as_variable(&self) -> Option<&dap::Variable> { self.entry.as_variable() } fn as_scope(&self) -> Option<&dap::Scope> { self.entry.as_scope() } fn item_id(&self) -> ElementId { use std::fmt::Write; let mut id = match &self.entry { DapEntry::Watcher(watcher) => format!("watcher-{}", watcher.expression), DapEntry::Variable(dap) => format!("variable-{}", dap.name), DapEntry::Scope(dap) => format!("scope-{}", dap.name), }; for name in self.path.indices.iter() { _ = write!(id, "-{}", name); } SharedString::from(id).into() } fn item_value_id(&self) -> ElementId { use std::fmt::Write; let mut id = match &self.entry { DapEntry::Watcher(watcher) => format!("watcher-{}", watcher.expression), DapEntry::Variable(dap) => format!("variable-{}", dap.name), DapEntry::Scope(dap) => format!("scope-{}", dap.name), }; for name in self.path.indices.iter() { _ = write!(id, "-{}", name); } _ = write!(id, "-value"); SharedString::from(id).into() }} struct VariableColor { name: Option<Hsla>, value: Option<Hsla>,} pub struct VariableList { entries: Vec<ListEntry>, entry_states: HashMap<EntryPath, EntryState>, selected_stack_frame_id: Option<StackFrameId>, list_handle: UniformListScrollHandle, session: Entity<Session>, selection: Option<EntryPath>, open_context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>, focus_handle: FocusHandle, edited_path: Option<(EntryPath, Entity<Editor>)>, disabled: bool, memory_view: Entity<MemoryView>, weak_running: WeakEntity<RunningState>, _subscriptions: Vec<Subscription>,} impl VariableList { pub(crate) fn new( session: Entity<Session>, stack_frame_list: Entity<StackFrameList>, memory_view: Entity<MemoryView>, weak_running: WeakEntity<RunningState>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let focus_handle = cx.focus_handle(); let _subscriptions = vec![ cx.subscribe(&stack_frame_list, Self::handle_stack_frame_list_events), cx.subscribe(&session, |this, _, event, cx| match event { SessionEvent::Stopped(_) => { this.selection.take(); this.edited_path.take(); this.selected_stack_frame_id.take(); } SessionEvent::Variables | SessionEvent::Watchers => { this.build_entries(cx); } _ => {} }), cx.on_focus_out(&focus_handle, window, |this, _, _, cx| { this.edited_path.take(); cx.notify(); }), ]; let list_state = UniformListScrollHandle::default(); Self { list_handle: list_state, session, focus_handle, _subscriptions, selected_stack_frame_id: None, selection: None, open_context_menu: None, disabled: false, edited_path: None, entries: Default::default(), entry_states: Default::default(), weak_running, memory_view, } } pub(super) fn disabled(&mut self, disabled: bool, cx: &mut Context<Self>) { let old_disabled = std::mem::take(&mut self.disabled); self.disabled = disabled; if old_disabled != disabled { cx.notify(); } } pub(super) fn has_open_context_menu(&self) -> bool { self.open_context_menu.is_some() } fn build_entries(&mut self, cx: &mut Context<Self>) { let Some(stack_frame_id) = self.selected_stack_frame_id else { return; }; let mut entries = vec![]; let scopes: Vec<_> = self.session.update(cx, |session, cx| { session.scopes(stack_frame_id, cx).to_vec() }); let mut contains_local_scope = false; let mut stack = scopes .into_iter() .rev() .filter(|scope| { if scope .presentation_hint .as_ref() .map(|hint| *hint == ScopePresentationHint::Locals) .unwrap_or(scope.name.to_lowercase().starts_with("local")) { contains_local_scope = true; } self.session.update(cx, |session, cx| { !session.variables(scope.variables_reference, cx).is_empty() }) }) .map(|scope| { ( scope.variables_reference, scope.variables_reference, EntryPath::for_scope(&scope.name), DapEntry::Scope(scope), ) }) .collect::<Vec<_>>(); let watches = self.session.read(cx).watchers().clone(); stack.extend( watches .into_values() .map(|watcher| { ( watcher.variables_reference, watcher.variables_reference, EntryPath::for_watcher(watcher.expression.clone()), DapEntry::Watcher(watcher), ) }) .collect::<Vec<_>>(), ); let scopes_count = stack.len(); while let Some((container_reference, variables_reference, mut path, dap_kind)) = stack.pop() { match &dap_kind { DapEntry::Watcher(watcher) => path = path.with_child(watcher.expression.clone()), DapEntry::Variable(dap) => path = path.with_name(dap.name.clone().into()), DapEntry::Scope(dap) => path = path.with_child(dap.name.clone().into()), } let var_state = self .entry_states .entry(path.clone()) .and_modify(|state| { state.parent_reference = container_reference; state.has_children = variables_reference != 0; }) .or_insert(EntryState { depth: path.indices.len(), is_expanded: dap_kind.as_scope().is_some_and(|scope| { (scopes_count == 1 && !contains_local_scope) || scope .presentation_hint .as_ref() .map(|hint| *hint == ScopePresentationHint::Locals) .unwrap_or(scope.name.to_lowercase().starts_with("local")) }), parent_reference: container_reference, has_children: variables_reference != 0, }); entries.push(ListEntry { entry: dap_kind, path: path.clone(), }); if var_state.is_expanded { let children = self .session .update(cx, |session, cx| session.variables(variables_reference, cx)); stack.extend(children.into_iter().rev().map(|child| { ( variables_reference, child.variables_reference, path.with_child(child.name.clone().into()), DapEntry::Variable(child), ) })); } } self.entries = entries; cx.notify(); } fn handle_stack_frame_list_events( &mut self, _: Entity<StackFrameList>, event: &StackFrameListEvent, cx: &mut Context<Self>, ) { match event { StackFrameListEvent::SelectedStackFrameChanged(stack_frame_id) => { self.selected_stack_frame_id = Some(*stack_frame_id); self.session.update(cx, |session, cx| { session.refresh_watchers(*stack_frame_id, cx); }); self.build_entries(cx); } StackFrameListEvent::BuiltEntries => {} } } pub fn completion_variables(&self, _cx: &mut Context<Self>) -> Vec<dap::Variable> { self.entries .iter() .filter_map(|entry| match &entry.entry { DapEntry::Variable(dap) => Some(dap.clone()), DapEntry::Scope(_) | DapEntry::Watcher { .. } => None, }) .collect() } fn render_entries( &mut self, ix: Range<usize>, window: &mut Window, cx: &mut Context<Self>, ) -> Vec<AnyElement> { ix.into_iter() .filter_map(|ix| { let (entry, state) = self .entries .get(ix) .and_then(|entry| Some(entry).zip(self.entry_states.get(&entry.path)))?; match &entry.entry { DapEntry::Watcher { .. } => { Some(self.render_watcher(entry, *state, window, cx)) } DapEntry::Variable(_) => Some(self.render_variable(entry, *state, window, cx)), DapEntry::Scope(_) => Some(self.render_scope(entry, *state, cx)), } }) .collect() } pub(crate) fn toggle_entry(&mut self, var_path: &EntryPath, cx: &mut Context<Self>) { let Some(entry) = self.entry_states.get_mut(var_path) else { log::error!("Could not find variable list entry state to toggle"); return; }; entry.is_expanded = !entry.is_expanded; self.build_entries(cx); } fn select_first(&mut self, _: &SelectFirst, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(variable) = self.entries.first() { self.selection = Some(variable.path.clone()); self.build_entries(cx); } } fn select_last(&mut self, _: &SelectLast, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(variable) = self.entries.last() { self.selection = Some(variable.path.clone()); self.build_entries(cx); } } fn select_prev(&mut self, _: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(selection) = &self.selection { let index = self.entries.iter().enumerate().find_map(|(ix, var)| { if &var.path == selection && ix > 0 { Some(ix.saturating_sub(1)) } else { None } }); if let Some(new_selection) = index.and_then(|ix| self.entries.get(ix).map(|var| var.path.clone())) { self.selection = Some(new_selection); self.build_entries(cx); } else { self.select_last(&SelectLast, window, cx); } } else { self.select_last(&SelectLast, window, cx); } } fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(selection) = &self.selection { let index = self.entries.iter().enumerate().find_map(|(ix, var)| { if &var.path == selection { Some(ix.saturating_add(1)) } else { None } }); if let Some(new_selection) = index.and_then(|ix| self.entries.get(ix).map(|var| var.path.clone())) { self.selection = Some(new_selection); self.build_entries(cx); } else { self.select_first(&SelectFirst, window, cx); } } else { self.select_first(&SelectFirst, window, cx); } } fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {Lines 1–500 of 1622. Load more
use crate::session::running::{RunningState, memory_view::MemoryView}; use super::stack_frame_list::{StackFrameList, StackFrameListEvent}; use dap::{ ScopePresentationHint, StackFrameId, VariablePresentationHint, VariablePresentationHintKind, VariableReference, }; use editor::Editor; use gpui::{ Action, AnyElement, ClickEvent, ClipboardItem, Context, DismissEvent, Empty, Entity, FocusHandle, Focusable, Hsla, MouseDownEvent, Point, Subscription, TextStyleRefinement, UniformListScrollHandle, WeakEntity, actions, anchored, deferred, uniform_list, }; use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious}; use project::debugger::{ dap_command::DataBreakpointContext, session::{Session, SessionEvent, Watcher}, }; use std::{collections::HashMap, ops::Range, sync::Arc}; use ui::{ContextMenu, ListItem, ScrollableHandle, Tooltip, WithScrollbar, prelude::*}; use util::{debug_panic, maybe}; actions!( variable_list, [ /// Expands the selected variable entry to show its children. ExpandSelectedEntry, /// Collapses the selected variable entry to hide its children. CollapseSelectedEntry, /// Copies the variable name to the clipboard. CopyVariableName, /// Copies the variable value to the clipboard. CopyVariableValue, /// Edits the value of the selected variable. EditVariable, /// Adds the selected variable to the watch list. AddWatch, /// Removes the selected variable from the watch list. RemoveWatch, /// Jump to variable's memory location. GoToMemory, ] ); #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) struct EntryState { depth: usize, is_expanded: bool, has_children: bool, parent_reference: VariableReference, } #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub(crate) struct EntryPath { pub leaf_name: Option<SharedString>, pub indices: Arc<[SharedString]>, } impl EntryPath { fn for_watcher(expression: impl Into<SharedString>) -> Self { Self { leaf_name: Some(expression.into()), indices: Arc::new([]), } } fn for_scope(scope_name: impl Into<SharedString>) -> Self { Self { leaf_name: Some(scope_name.into()), indices: Arc::new([]), } } fn with_name(&self, name: SharedString) -> Self { Self { leaf_name: Some(name), indices: self.indices.clone(), } } /// Create a new child of this variable path fn with_child(&self, name: SharedString) -> Self { Self { leaf_name: None, indices: self .indices .iter() .cloned() .chain(std::iter::once(name)) .collect(), } } } #[derive(Debug, Clone, PartialEq)] enum DapEntry { Watcher(Watcher), Variable(dap::Variable), Scope(dap::Scope), } impl DapEntry { fn as_watcher(&self) -> Option<&Watcher> { match self { DapEntry::Watcher(watcher) => Some(watcher), _ => None, } } fn as_variable(&self) -> Option<&dap::Variable> { match self { DapEntry::Variable(dap) => Some(dap), _ => None, } } fn as_scope(&self) -> Option<&dap::Scope> { match self { DapEntry::Scope(dap) => Some(dap), _ => None, } } #[cfg(test)] fn name(&self) -> &str { match self { DapEntry::Watcher(watcher) => &watcher.expression, DapEntry::Variable(dap) => &dap.name, DapEntry::Scope(dap) => &dap.name, } } } #[derive(Debug, Clone, PartialEq)] struct ListEntry { entry: DapEntry, path: EntryPath, } impl ListEntry { fn as_watcher(&self) -> Option<&Watcher> { self.entry.as_watcher() } fn as_variable(&self) -> Option<&dap::Variable> { self.entry.as_variable() } fn as_scope(&self) -> Option<&dap::Scope> { self.entry.as_scope() } fn item_id(&self) -> ElementId { use std::fmt::Write; let mut id = match &self.entry { DapEntry::Watcher(watcher) => format!("watcher-{}", watcher.expression), DapEntry::Variable(dap) => format!("variable-{}", dap.name), DapEntry::Scope(dap) => format!("scope-{}", dap.name), }; for name in self.path.indices.iter() { _ = write!(id, "-{}", name); } SharedString::from(id).into() } fn item_value_id(&self) -> ElementId { use std::fmt::Write; let mut id = match &self.entry { DapEntry::Watcher(watcher) => format!("watcher-{}", watcher.expression), DapEntry::Variable(dap) => format!("variable-{}", dap.name), DapEntry::Scope(dap) => format!("scope-{}", dap.name), }; for name in self.path.indices.iter() { _ = write!(id, "-{}", name); } _ = write!(id, "-value"); SharedString::from(id).into() } } struct VariableColor { name: Option<Hsla>, value: Option<Hsla>, } pub struct VariableList { entries: Vec<ListEntry>, entry_states: HashMap<EntryPath, EntryState>, selected_stack_frame_id: Option<StackFrameId>, list_handle: UniformListScrollHandle, session: Entity<Session>, selection: Option<EntryPath>, open_context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>, focus_handle: FocusHandle, edited_path: Option<(EntryPath, Entity<Editor>)>, disabled: bool, memory_view: Entity<MemoryView>, weak_running: WeakEntity<RunningState>, _subscriptions: Vec<Subscription>, } impl VariableList { pub(crate) fn new( session: Entity<Session>, stack_frame_list: Entity<StackFrameList>, memory_view: Entity<MemoryView>, weak_running: WeakEntity<RunningState>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let focus_handle = cx.focus_handle(); let _subscriptions = vec![ cx.subscribe(&stack_frame_list, Self::handle_stack_frame_list_events), cx.subscribe(&session, |this, _, event, cx| match event { SessionEvent::Stopped(_) => { this.selection.take(); this.edited_path.take(); this.selected_stack_frame_id.take(); } SessionEvent::Variables | SessionEvent::Watchers => { this.build_entries(cx); } _ => {} }), cx.on_focus_out(&focus_handle, window, |this, _, _, cx| { this.edited_path.take(); cx.notify(); }), ]; let list_state = UniformListScrollHandle::default(); Self { list_handle: list_state, session, focus_handle, _subscriptions, selected_stack_frame_id: None, selection: None, open_context_menu: None, disabled: false, edited_path: None, entries: Default::default(), entry_states: Default::default(), weak_running, memory_view, } } pub(super) fn disabled(&mut self, disabled: bool, cx: &mut Context<Self>) { let old_disabled = std::mem::take(&mut self.disabled); self.disabled = disabled; if old_disabled != disabled { cx.notify(); } } pub(super) fn has_open_context_menu(&self) -> bool { self.open_context_menu.is_some() } fn build_entries(&mut self, cx: &mut Context<Self>) { let Some(stack_frame_id) = self.selected_stack_frame_id else { return; }; let mut entries = vec![]; let scopes: Vec<_> = self.session.update(cx, |session, cx| { session.scopes(stack_frame_id, cx).to_vec() }); let mut contains_local_scope = false; let mut stack = scopes .into_iter() .rev() .filter(|scope| { if scope .presentation_hint .as_ref() .map(|hint| *hint == ScopePresentationHint::Locals) .unwrap_or(scope.name.to_lowercase().starts_with("local")) { contains_local_scope = true; } self.session.update(cx, |session, cx| { !session.variables(scope.variables_reference, cx).is_empty() }) }) .map(|scope| { ( scope.variables_reference, scope.variables_reference, EntryPath::for_scope(&scope.name), DapEntry::Scope(scope), ) }) .collect::<Vec<_>>(); let watches = self.session.read(cx).watchers().clone(); stack.extend( watches .into_values() .map(|watcher| { ( watcher.variables_reference, watcher.variables_reference, EntryPath::for_watcher(watcher.expression.clone()), DapEntry::Watcher(watcher), ) }) .collect::<Vec<_>>(), ); let scopes_count = stack.len(); while let Some((container_reference, variables_reference, mut path, dap_kind)) = stack.pop() { match &dap_kind { DapEntry::Watcher(watcher) => path = path.with_child(watcher.expression.clone()), DapEntry::Variable(dap) => path = path.with_name(dap.name.clone().into()), DapEntry::Scope(dap) => path = path.with_child(dap.name.clone().into()), } let var_state = self .entry_states .entry(path.clone()) .and_modify(|state| { state.parent_reference = container_reference; state.has_children = variables_reference != 0; }) .or_insert(EntryState { depth: path.indices.len(), is_expanded: dap_kind.as_scope().is_some_and(|scope| { (scopes_count == 1 && !contains_local_scope) || scope .presentation_hint .as_ref() .map(|hint| *hint == ScopePresentationHint::Locals) .unwrap_or(scope.name.to_lowercase().starts_with("local")) }), parent_reference: container_reference, has_children: variables_reference != 0, }); entries.push(ListEntry { entry: dap_kind, path: path.clone(), }); if var_state.is_expanded { let children = self .session .update(cx, |session, cx| session.variables(variables_reference, cx)); stack.extend(children.into_iter().rev().map(|child| { ( variables_reference, child.variables_reference, path.with_child(child.name.clone().into()), DapEntry::Variable(child), ) })); } } self.entries = entries; cx.notify(); } fn handle_stack_frame_list_events( &mut self, _: Entity<StackFrameList>, event: &StackFrameListEvent, cx: &mut Context<Self>, ) { match event { StackFrameListEvent::SelectedStackFrameChanged(stack_frame_id) => { self.selected_stack_frame_id = Some(*stack_frame_id); self.session.update(cx, |session, cx| { session.refresh_watchers(*stack_frame_id, cx); }); self.build_entries(cx); } StackFrameListEvent::BuiltEntries => {} } } pub fn completion_variables(&self, _cx: &mut Context<Self>) -> Vec<dap::Variable> { self.entries .iter() .filter_map(|entry| match &entry.entry { DapEntry::Variable(dap) => Some(dap.clone()), DapEntry::Scope(_) | DapEntry::Watcher { .. } => None, }) .collect() } fn render_entries( &mut self, ix: Range<usize>, window: &mut Window, cx: &mut Context<Self>, ) -> Vec<AnyElement> { ix.into_iter() .filter_map(|ix| { let (entry, state) = self .entries .get(ix) .and_then(|entry| Some(entry).zip(self.entry_states.get(&entry.path)))?; match &entry.entry { DapEntry::Watcher { .. } => { Some(self.render_watcher(entry, *state, window, cx)) } DapEntry::Variable(_) => Some(self.render_variable(entry, *state, window, cx)), DapEntry::Scope(_) => Some(self.render_scope(entry, *state, cx)), } }) .collect() } pub(crate) fn toggle_entry(&mut self, var_path: &EntryPath, cx: &mut Context<Self>) { let Some(entry) = self.entry_states.get_mut(var_path) else { log::error!("Could not find variable list entry state to toggle"); return; }; entry.is_expanded = !entry.is_expanded; self.build_entries(cx); } fn select_first(&mut self, _: &SelectFirst, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(variable) = self.entries.first() { self.selection = Some(variable.path.clone()); self.build_entries(cx); } } fn select_last(&mut self, _: &SelectLast, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(variable) = self.entries.last() { self.selection = Some(variable.path.clone()); self.build_entries(cx); } } fn select_prev(&mut self, _: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(selection) = &self.selection { let index = self.entries.iter().enumerate().find_map(|(ix, var)| { if &var.path == selection && ix > 0 { Some(ix.saturating_sub(1)) } else { None } }); if let Some(new_selection) = index.and_then(|ix| self.entries.get(ix).map(|var| var.path.clone())) { self.selection = Some(new_selection); self.build_entries(cx); } else { self.select_last(&SelectLast, window, cx); } } else { self.select_last(&SelectLast, window, cx); } } fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) { self.cancel(&Default::default(), window, cx); if let Some(selection) = &self.selection { let index = self.entries.iter().enumerate().find_map(|(ix, var)| { if &var.path == selection { Some(ix.saturating_add(1)) } else { None } }); if let Some(new_selection) = index.and_then(|ix| self.entries.get(ix).map(|var| var.path.clone())) { self.selection = Some(new_selection); self.build_entries(cx); } else { self.select_first(&SelectFirst, window, cx); } } else { self.select_first(&SelectFirst, window, cx); } } fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {