diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ba0296..be2c220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ main ---- - Improved property rendering -- Introduced theme concept +- Introduced themes (including solarized and solarized dark) - Show value of variables on current line - Support `extended_properties` #5 diff --git a/README.md b/README.md index e3e9c2e..67a2948 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Debug-TUI ========= -Interactive XDebug step debugger for your terminal with vim-like key bindings. +Interactive [Xdebug](https://xdebug.org) step-debugging client your terminal. ![Screenshot](https://github.com/user-attachments/assets/9f938d2b-717b-4816-bb35-9f317f82a0a3) @@ -38,11 +38,12 @@ Prefix with number to repeat: - `-` decrease context depth - `tab` switch pane - `enter` toggle pane focus (full screen) +- `t` rotate the theme - `?` Show help ## Setting Breakpoints -`xdebug-tui` has no mechanism for setting a breakpoint but you can use the +`debug-tui` has no mechanism for setting a breakpoint but you can use the function `xdebug_break()` in your code: ```php diff --git a/php/test.php b/php/test.php index cf17f5a..467b777 100644 --- a/php/test.php +++ b/php/test.php @@ -1,6 +1,6 @@ 123, 'float' => 123.4, 'string' => "string", @@ -14,7 +14,7 @@ ]; $a = 2; $foo = $a; -$foo = $arra😸ay; +$foo = $array; $bar = $foo; diff --git a/src/app.rs b/src/app.rs index 30969d2..54536d1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -175,7 +175,7 @@ impl App { counter: 0, context_depth: 4, - theme: Theme::Dark, + theme: Theme::SolarizedDark, server_status: None, command_input: Input::default(), command_response: None, @@ -380,6 +380,10 @@ impl App { AppEvent::PushInputPlurality(char) => self.input_plurality.push(char), AppEvent::Input(key_event) => { match key_event.code { + KeyCode::Char('t') => { + self.theme = self.theme.next(); + self.notification = Notification::info(format!("Switched to theme: {:?}", self.theme)); + }, KeyCode::Char('?') => { self.sender.send(AppEvent::ChangeView(CurrentView::Help)).await.unwrap(); }, diff --git a/src/event/input.rs b/src/event/input.rs index ae55909..aef9c3f 100644 --- a/src/event/input.rs +++ b/src/event/input.rs @@ -45,6 +45,7 @@ pub enum AppEvent { ScrollStack(i16), PushInputPlurality(char), ContextDepth(i8), + NextTheme, } pub type EventSender = Sender; diff --git a/src/theme.rs b/src/theme.rs index e917c48..be8c820 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,13 +1,63 @@ -use ratatui::style::{Color, Style}; +use ratatui::style::Color; +use ratatui::style::Style; +#[derive(Debug)] pub enum Theme { Dark, + SolarizedDark, + SolarizedLight, } impl Theme { + pub fn next(&self) -> Theme { + match self { + Theme::Dark => Self::SolarizedDark, + Theme::SolarizedDark => Self::SolarizedLight, + Theme::SolarizedLight => Self::Dark, + } + } pub fn scheme(&self) -> Scheme { match self { - Theme::Dark => Scheme{ + Theme::SolarizedLight => { + let mut scheme = Self::SolarizedDark.scheme(); + scheme.background = Style::default().bg(Solarized::Base3.to_color()); + + scheme.widget_active = Style::default().fg(Solarized::Green.to_color()).bg(Solarized::Base2.to_color()); + scheme.widget_inactive = Style::default().fg(Solarized::Base03.to_color()).bg(Solarized::Base3.to_color()); + scheme.source_line = scheme.source_line.fg(Solarized::Base00.to_color()); + scheme.source_line_highlight = scheme.source_line_highlight.bg(Solarized::Base2.to_color()).fg(Solarized::Base01.to_color()); + + scheme.widget_mode_debug = scheme.widget_mode_debug.fg(Solarized::Base03.to_color()).bg(Solarized::Base3.to_color()); + scheme.widget_mode_history = scheme.widget_mode_debug.bg(Solarized::Base3.to_color()).fg(Solarized::Red.to_color()); + + return scheme; + } + Theme::SolarizedDark => Scheme{ + syntax_variable: Style::default().fg(Solarized::Base00.to_color()), + syntax_type: Style::default().fg(Solarized::Cyan.to_color()), + syntax_type_object: Style::default().fg(Solarized::Orange.to_color()), + syntax_literal: Style::default().fg(Solarized::Blue.to_color()), + syntax_literal_string: Style::default().fg(Solarized::Green.to_color()), + syntax_label: Style::default().fg(Solarized::Base00.to_color()), + 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()), + 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()), + source_line_no: Style::default().fg(Solarized::Yellow.to_color()), + source_line_highlight: Style::default().bg(Solarized::Base02.to_color()).fg(Solarized::Base3.to_color()), + source_annotation: Style::default().fg(Solarized::Magenta.to_color()), + stack_line: Style::default().fg(Solarized::Base1.to_color()), + + widget_active: Style::default().fg(Solarized::Base02.to_color()).bg(Solarized::Green.to_color()), + widget_inactive: Style::default().fg(Solarized::Base1.to_color()).bg(Solarized::Base03.to_color()), + widget_mode_debug: Style::default().fg(Solarized::Base1.to_color()).bg(Solarized::Base03.to_color()), + widget_mode_history: Style::default().fg(Solarized::Red.to_color()).bg(Solarized::Base03.to_color()), + + background: Style::default().bg(Color::Black), + }, + Theme::Dark => Scheme { syntax_variable: Style::default().fg(Color::LightBlue), syntax_type: Style::default().fg(Color::LightRed), syntax_type_object: Style::default().fg(Color::LightMagenta), @@ -22,18 +72,25 @@ impl Theme { pane_border_active: Style::default().fg(Color::Green), pane_border_inactive: Style::default().fg(Color::DarkGray), - source_line: Style::default(), + source_line: Style::default().fg(Color::White), source_line_no: Style::default().fg(Color::Yellow), source_line_highlight: Style::default().bg(Color::Blue), source_annotation: Style::default().fg(Color::DarkGray), stack_line: Style::default().fg(Color::White), + + widget_active: Style::default().fg(Color::Black).bg(Color::Green), + widget_inactive: Style::default().fg(Color::Black).bg(Color::Yellow), + widget_mode_debug: Style::default().bg(Color::Blue), + widget_mode_history: Style::default().bg(Color::Red), + background: Style::default().bg(Color::Black), }, } } } pub struct Scheme { + pub background: Style, pub syntax_variable: Style, pub syntax_type: Style, pub syntax_type_object: Style, @@ -54,9 +111,53 @@ pub struct Scheme { pub source_annotation: Style, pub stack_line: Style, -} -pub enum Role { + pub widget_active: Style, + pub widget_inactive: Style, + pub widget_mode_debug: Style, + pub widget_mode_history: Style, } +pub enum Role {} + +pub enum Solarized { + Base03, + Base02, + Base01, + Base00, + Base0, + Base1, + Base2, + Base3, + Yellow, + Orange, + Red, + Magenta, + Violet, + Blue, + Cyan, + Green, +} +impl Solarized { + fn to_color(&self) -> Color { + match self { + Solarized::Base03 => Color::Rgb(0, 43, 54), + Solarized::Base02 => Color::Rgb(7, 54, 66), + Solarized::Base01 => Color::Rgb(88, 110, 117), + Solarized::Base00 => Color::Rgb(101, 123, 131), + Solarized::Base0 => Color::Rgb(131, 148, 150), + Solarized::Base1 => Color::Rgb(147, 161, 161), + Solarized::Base2 => Color::Rgb(238, 232, 213), + Solarized::Base3 => Color::Rgb(253, 246, 227), + Solarized::Yellow => Color::Rgb(181, 137, 0), + Solarized::Orange => Color::Rgb(203, 75, 22), + Solarized::Red => Color::Rgb(220, 50, 47), + Solarized::Magenta => Color::Rgb(211, 54, 130), + Solarized::Violet => Color::Rgb(108, 113, 196), + Solarized::Blue => Color::Rgb(38, 139, 210), + Solarized::Cyan => Color::Rgb(42, 161, 152), + Solarized::Green => Color::Rgb(133, 153, 0), + } + } +} diff --git a/src/view/help.rs b/src/view/help.rs index 4c68cac..0f0d5de 100644 --- a/src/view/help.rs +++ b/src/view/help.rs @@ -45,6 +45,7 @@ Key mappings (prefix with number to repeat): [K] scroll up 10 [+] increase context depth [-] decrease context depth +[t] rotate the theme [enter] toggle pane focus (full screen) Legend: diff --git a/src/view/layout.rs b/src/view/layout.rs index 5797bb3..a5cb9d4 100644 --- a/src/view/layout.rs +++ b/src/view/layout.rs @@ -10,13 +10,10 @@ use crate::notification::NotificationLevel; use ratatui::layout::Constraint; use ratatui::layout::Layout; use ratatui::layout::Rect; -use ratatui::style::Color; -use ratatui::style::Modifier; use ratatui::style::Style; -use ratatui::style::Stylize; use ratatui::text::Line; use ratatui::text::Span; -use ratatui::widgets::Clear; +use ratatui::widgets::Block; use ratatui::widgets::Paragraph; use ratatui::Frame; @@ -28,17 +25,14 @@ impl View for LayoutView { } fn draw(app: &App, f: &mut Frame, area: Rect) { - let constraints = vec![ - Constraint::Length(1), - Constraint::Min(4), - ]; + let constraints = vec![Constraint::Length(1), Constraint::Min(4)]; let rows = Layout::default() .margin(0) .constraints(constraints) .split(area); - f.render_widget(Clear::default(), rows[0]); + f.render_widget(Block::default().style(app.theme().background), area); f.render_widget(status_widget(app), rows[0]); match app.view_current { @@ -54,47 +48,43 @@ fn status_widget(app: &App) -> Paragraph { Span::styled( format!( " 󱘖 {} ", - if app.is_connected { "".to_string() } else { app.config.listen.to_string() } + if app.is_connected { + "connected".to_string() + } else { + app.config.listen.to_string() + } ), - Style::default() - .add_modifier(Modifier::BOLD) - .bg(match app.is_connected { - false => Color::Yellow, - true => Color::Green, - }) - .fg(match app.is_connected { - false => Color::Black, - true => Color::Black, - }), + match app.is_connected { + false => app.theme().widget_inactive, + true => app.theme().widget_active, + }, ), Span::styled( - format!("  {:<3} ", app.history.current().map_or("n/a".to_string(), |entry| { - entry.stack.depth().to_string() - })), - Style::default() - .bg(Color::Magenta) - .bold() - .fg(Color::White), - + format!( + "  {:<3} ", + app.history.current().map_or("n/a".to_string(), |entry| { + entry.stack.depth().to_string() + }) + ), + app.theme().widget_inactive, ), Span::styled( (match app.session_view.mode { - SessionViewMode::Current => match app.is_connected { - true => format!("  {} / ∞", app.history.offset + 1), - false => "  0 / 0".to_string(), - }, - SessionViewMode::History => format!( + SessionViewMode::Current => match app.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() ), - }).to_string(), - Style::default().bg( - match app.session_view.mode { - SessionViewMode::Current => Color::Blue, - SessionViewMode::History => Color::Red, - } - ), + }) + .to_string(), + match app.session_view.mode { + SessionViewMode::Current => app.theme().widget_mode_debug, + SessionViewMode::History => app.theme().widget_mode_history, + }, ), Span::styled( match app.notification.is_visible() { diff --git a/src/view/session.rs b/src/view/session.rs index 0690a73..a594fca 100644 --- a/src/view/session.rs +++ b/src/view/session.rs @@ -13,7 +13,6 @@ use ratatui::layout::Layout; use ratatui::layout::Rect; use ratatui::widgets::Block; use ratatui::widgets::Borders; -use ratatui::widgets::Clear; use ratatui::Frame; pub struct SessionView {} @@ -131,7 +130,6 @@ fn build_pane_widget(frame: &mut Frame, app: &App, pane: &Pane, area: Rect, inde ); frame.render_widget(&block, area); - frame.render_widget(Clear::default(), block.inner(area)); match pane.component_type { ComponentType::Source => {