diff --git a/CHANGELOG.md b/CHANGELOG.md index 6683456..a55c308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG main ---- +- Do not accept connections while an existing session is running - Filter properties with dot notation in context pane (`f`) - Stack traversal - select stack and inspect stack frames in current mode. - Fixed light theme. diff --git a/src/app.rs b/src/app.rs index 6572986..19ff680 100644 --- a/src/app.rs +++ b/src/app.rs @@ -175,12 +175,26 @@ pub enum SelectedView { Help, } +#[derive(PartialEq)] +pub enum ListenStatus { + Connected, + Listening, + Refusing, +} + +impl ListenStatus { + pub fn is_connected(&self) -> bool { + *self == ListenStatus::Connected + } + +} + pub struct App { receiver: Receiver, quit: bool, sender: Sender, - pub is_connected: bool, + pub listening_status: ListenStatus, pub notification: Notification, pub config: Config, @@ -212,7 +226,7 @@ impl App { pub fn new(config: Config, receiver: Receiver, sender: Sender) -> App { let client = Arc::new(Mutex::new(DbgpClient::new(None))); App { - is_connected: false, + listening_status: ListenStatus::Listening, config, input_plurality: vec![], notification: Notification::none(), @@ -346,7 +360,7 @@ impl App { AppEvent::HistoryNext => { for _ in 0..self.take_motion() { self.history.next(); - if self.history.is_current() && self.is_connected { + if self.history.is_current() && (self.listening_status == ListenStatus::Connected) { self.sender .send(AppEvent::ChangeSessionViewMode(SessionViewMode::Current)) .await?; @@ -358,26 +372,36 @@ impl App { self.history.previous(); } } + AppEvent::Listen => { + self.listening_status = ListenStatus::Listening; + self.view_current = SelectedView::Listen; + self.session_view.mode = SessionViewMode::Current; + self.notification = Notification::info("listening for next connection".to_string()) + }, AppEvent::ClientConnected(s) => { - let filepath = { - let mut client = self.client.lock().await; - let response = client.deref_mut().connect(s).await?; - for (feature, value) in [ - ("max_depth", self.context_depth.to_string().as_str()), - ("extended_properties", "1"), - ] { - info!("setting feature {} to {:?}", feature, value); - client.feature_set(feature, value).await?; - } - response.fileuri.clone() - }; - self.is_connected = true; - self.reset(); - - let source = self.workspace.open(filepath.clone()).await; - self.history = History::default(); - self.history - .push(HistoryEntry::initial(filepath.clone(), source.text.clone())); + if self.listening_status != ListenStatus::Listening { + self.notification = Notification::warning("refused incoming connection".to_string()); + } else { + let filepath = { + let mut client = self.client.lock().await; + let response = client.deref_mut().connect(s).await?; + for (feature, value) in [ + ("max_depth", self.context_depth.to_string().as_str()), + ("extended_properties", "1"), + ] { + info!("setting feature {} to {:?}", feature, value); + client.feature_set(feature, value).await?; + } + response.fileuri.clone() + }; + self.listening_status = ListenStatus::Connected; + self.reset(); + + let source = self.workspace.open(filepath.clone()).await; + self.history = History::default(); + self.history + .push(HistoryEntry::initial(filepath.clone(), source.text.clone())); + } } AppEvent::Snapshot() => { self.snapshot().await?; @@ -445,7 +469,7 @@ impl App { } AppEvent::Disconnect => { let _ = self.client.lock().await.deref_mut().disonnect().await; - self.is_connected = false; + self.listening_status = ListenStatus::Refusing; self.sender .send(AppEvent::ChangeSessionViewMode(SessionViewMode::History)) .await?; @@ -575,7 +599,7 @@ impl App { let line_no = frame.line; let context = { match (level as u16) < self.stack_max_context_fetch { - true => Some(self.client.lock().await.deref_mut().context_get(level as u16).await.unwrap()), + true => Some(self.client.lock().await.deref_mut().context_get(level as u16).await?), false => None, } }; diff --git a/src/event/input.rs b/src/event/input.rs index 92cd52e..664fbba 100644 --- a/src/event/input.rs +++ b/src/event/input.rs @@ -49,6 +49,7 @@ pub enum AppEvent { NextTheme, ContextFilterOpen, ContextSearchClose, + Listen, } pub type EventSender = Sender; diff --git a/src/notification.rs b/src/notification.rs index 9ea34e3..2631b11 100644 --- a/src/notification.rs +++ b/src/notification.rs @@ -5,6 +5,7 @@ pub enum NotificationLevel { Error, None, Info, + Warning, } pub struct Notification { pub message: String, @@ -44,4 +45,15 @@ impl Notification { .unwrap(), } } + + #[allow(dead_code)] + pub(crate) fn warning(message: String) -> Notification { + Notification { + message, + level: NotificationLevel::Warning, + expires: SystemTime::now() + .checked_add(Duration::from_secs(5)) + .unwrap(), + } + } } diff --git a/src/theme.rs b/src/theme.rs index f7471c2..7907b26 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,3 +1,4 @@ + use ratatui::style::Color; use ratatui::style::Style; @@ -47,6 +48,7 @@ impl Theme { syntax_brace: Style::default().fg(Solarized::Base01.to_color()), notification_info: Style::default().fg(Solarized::Green.to_color()), notification_error: Style::default().fg(Solarized::Red.to_color()), + notification_warning: Style::default().fg(Color::Yellow), pane_border_active: Style::default().fg(Solarized::Base01.to_color()), pane_border_inactive: Style::default().fg(Solarized::Base02.to_color()), source_line: Style::default().fg(Solarized::Base1.to_color()), @@ -73,7 +75,8 @@ impl Theme { syntax_brace: Style::default().fg(Color::White), notification_info: Style::default().fg(Color::Black).bg(Color::Green), - notification_error: Style::default().fg(Color::White).bg(Color::Red), + notification_error: Style::default().fg(Color::Yellow), + notification_warning: Style::default().fg(Color::White).bg(Color::Yellow), pane_border_active: Style::default().fg(Color::Green), pane_border_inactive: Style::default().fg(Color::DarkGray), @@ -108,6 +111,7 @@ pub struct Scheme { pub notification_info: Style, pub notification_error: Style, + pub notification_warning: Style, pub pane_border_active: Style, pub pane_border_inactive: Style, diff --git a/src/view/context.rs b/src/view/context.rs index eefc4b8..1e58032 100644 --- a/src/view/context.rs +++ b/src/view/context.rs @@ -232,7 +232,7 @@ mod test { ], &mut lines, 0, - &mut filter, + filter, ); assert_eq!(vec![ diff --git a/src/view/help.rs b/src/view/help.rs index 6c736ff..aef43a5 100644 --- a/src/view/help.rs +++ b/src/view/help.rs @@ -1,5 +1,5 @@ use super::View; -use crate::app::{App, SelectedView}; +use crate::app::{App, ListenStatus, SelectedView}; use crate::event::input::AppEvent; use ratatui::layout::Rect; use ratatui::widgets::Paragraph; @@ -11,7 +11,7 @@ impl View for HelpView { fn handle(app: &mut App, event: AppEvent) -> Option { match event { AppEvent::Input(_) => { - if app.is_connected { + if app.listening_status == ListenStatus::Connected{ Some(AppEvent::ChangeView(SelectedView::Session)) } else { Some(AppEvent::ChangeView(SelectedView::Listen)) diff --git a/src/view/layout.rs b/src/view/layout.rs index b2acbfe..1b19ece 100644 --- a/src/view/layout.rs +++ b/src/view/layout.rs @@ -4,6 +4,7 @@ use super::session::SessionView; use super::session::SessionViewMode; use super::View; use crate::app::App; +use crate::app::ListenStatus; use crate::app::SelectedView; use crate::event::input::AppEvent; use crate::notification::NotificationLevel; @@ -48,13 +49,14 @@ fn status_widget(app: &App) -> Paragraph { Span::styled( format!( " 󱘖 {} ", - if app.is_connected { - "connected".to_string() - } else { - app.config.listen.to_string() - } + match app.listening_status { + ListenStatus::Connected => "connected".to_string(), + + ListenStatus::Listening => app.config.listen.to_string(), + ListenStatus::Refusing => "refusing".to_string(), + }, ), - match app.is_connected { + match app.listening_status.is_connected() { false => app.theme().widget_inactive, true => app.theme().widget_active, }, @@ -70,15 +72,25 @@ fn status_widget(app: &App) -> Paragraph { ), Span::styled( (match app.session_view.mode { - SessionViewMode::Current => match app.is_connected { + SessionViewMode::Current => match app.listening_status.is_connected() { true => format!("  {} / ∞", app.history.offset + 1), false => "  0 / 0".to_string(), }, - SessionViewMode::History => format!( - "  {} / {} history [p] to go back [n] to go forwards [b] to return", - app.history.offset + 1, - app.history.len() - ), + SessionViewMode::History => { + match app.listening_status { + ListenStatus::Connected => format!( + "  {} / {} history [p] to go back [n] to go forwards [b] to return", + app.history.offset + 1, + app.history.len() + ), + ListenStatus::Refusing => format!( + "  {} / {} terminated [p] to go back [n] to go forwards [b] to listen", + app.history.offset + 1, + app.history.len() + ), + ListenStatus::Listening => String::new(), + } + } }) .to_string(), match app.session_view.mode { @@ -93,6 +105,7 @@ fn status_widget(app: &App) -> Paragraph { }, match app.notification.level { NotificationLevel::Error => app.theme().notification_error, + NotificationLevel::Warning => app.theme().notification_warning, NotificationLevel::Info => app.theme().notification_info, NotificationLevel::None => Style::default(), }, diff --git a/src/view/session.rs b/src/view/session.rs index 9679eb9..bd293b7 100644 --- a/src/view/session.rs +++ b/src/view/session.rs @@ -5,6 +5,7 @@ use super::ComponentType; use super::Pane; use super::View; use crate::app::App; +use crate::app::ListenStatus; use crate::app::SelectedView; use crate::event::input::AppEvent; use crossterm::event::KeyCode; @@ -68,7 +69,13 @@ impl View for SessionView { KeyCode::Char(c) => match c { 'n' => Some(AppEvent::HistoryNext), 'p' => Some(AppEvent::HistoryPrevious), - 'b' => Some(AppEvent::ChangeSessionViewMode(SessionViewMode::Current)), + 'b' => { + if app.listening_status == ListenStatus::Refusing { + Some(AppEvent::Listen) + } else { + Some(AppEvent::ChangeSessionViewMode(SessionViewMode::Current)) + } + } _ => None, }, _ => None,