1444 lines · 48.0 KB · Rust
RawDownload
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, MouseButton, MouseDownEvent, Point, Stateful, Subscription,    TextStyleRefinement, UniformListScrollHandle, actions, anchored, deferred, uniform_list,};use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious};use project::debugger::session::{Session, SessionEvent, Watcher};use std::{collections::HashMap, ops::Range, sync::Arc};use ui::{ContextMenu, ListItem, ScrollableHandle, Scrollbar, ScrollbarState, Tooltip, prelude::*};use util::debug_panic; actions!(    variable_list,    [        ExpandSelectedEntry,        CollapseSelectedEntry,        CopyVariableName,        CopyVariableValue,        EditVariable,        AddWatch,        RemoveWatch,    ]); #[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 EntryKind {    Watcher(Watcher),    Variable(dap::Variable),    Scope(dap::Scope),} impl EntryKind {    fn as_watcher(&self) -> Option<&Watcher> {        match self {            EntryKind::Watcher(watcher) => Some(watcher),            _ => None,        }    }     fn as_variable(&self) -> Option<&dap::Variable> {        match self {            EntryKind::Variable(dap) => Some(dap),            _ => None,        }    }     fn as_scope(&self) -> Option<&dap::Scope> {        match self {            EntryKind::Scope(dap) => Some(dap),            _ => None,        }    }     #[cfg(test)]    fn name(&self) -> &str {        match self {            EntryKind::Watcher(watcher) => &watcher.expression,            EntryKind::Variable(dap) => &dap.name,            EntryKind::Scope(dap) => &dap.name,        }    }} #[derive(Debug, Clone, PartialEq)]struct ListEntry {    dap_kind: EntryKind,    path: EntryPath,} impl ListEntry {    fn as_watcher(&self) -> Option<&Watcher> {        self.dap_kind.as_watcher()    }     fn as_variable(&self) -> Option<&dap::Variable> {        self.dap_kind.as_variable()    }     fn as_scope(&self) -> Option<&dap::Scope> {        self.dap_kind.as_scope()    }     fn item_id(&self) -> ElementId {        use std::fmt::Write;        let mut id = match &self.dap_kind {            EntryKind::Watcher(watcher) => format!("watcher-{}", watcher.expression),            EntryKind::Variable(dap) => format!("variable-{}", dap.name),            EntryKind::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.dap_kind {            EntryKind::Watcher(watcher) => format!("watcher-{}", watcher.expression),            EntryKind::Variable(dap) => format!("variable-{}", dap.name),            EntryKind::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,    scrollbar_state: ScrollbarState,    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,    _subscriptions: Vec<Subscription>,} impl VariableList {    pub fn new(        session: Entity<Session>,        stack_frame_list: Entity<StackFrameList>,        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 {            scrollbar_state: ScrollbarState::new(list_state.clone()),            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(),        }    }     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).iter().cloned().collect()        });         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).len() > 0                })            })            .map(|scope| {                (                    scope.variables_reference,                    scope.variables_reference,                    EntryPath::for_scope(&scope.name),                    EntryKind::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()),                        EntryKind::Watcher(watcher.clone()),                    )                })                .collect::<Vec<_>>(),        );         let scopes_count = stack.len();         while let Some((container_reference, variables_reference, mut path, dap_kind)) = stack.pop()        {            match &dap_kind {                EntryKind::Watcher(watcher) => path = path.with_child(watcher.expression.clone()),                EntryKind::Variable(dap) => path = path.with_name(dap.name.clone().into()),                EntryKind::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 {                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()),                        EntryKind::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.dap_kind {                EntryKind::Variable(dap) => Some(dap.clone()),                EntryKind::Scope(_) | EntryKind::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.dap_kind {                    EntryKind::Watcher { .. } => {                        Some(self.render_watcher(entry, *state, window, cx))                    }                    EntryKind::Variable(_) => Some(self.render_variable(entry, *state, window, cx)),                    EntryKind::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>) {        self.edited_path.take();        self.focus_handle.focus(window);        cx.notify();    }     fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {        if let Some((var_path, editor)) = self.edited_path.take() {            let Some(state) = self.entry_states.get(&var_path) else {                return;            };             let variables_reference = state.parent_reference;            let Some(name) = var_path.leaf_name else {                return;            };             let Some(stack_frame_id) = self.selected_stack_frame_id else {                return;            };

Lines 1–500 of 1444.