From 6158b750917487da00706b9c2cea135f0fd27242 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 16 Apr 2024 00:34:47 +0200 Subject: [PATCH 1/3] feat(debugger): update ratatui, use `List` --- Cargo.lock | 80 +++++++++-------- crates/debugger/Cargo.toml | 2 +- crates/debugger/src/tui/context.rs | 4 +- crates/debugger/src/tui/draw.rs | 134 +++++++++++------------------ crates/debugger/src/tui/mod.rs | 5 +- 5 files changed, 97 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f2446602731..e550e31f824a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ checksum = "40646aa7f01e396139cf0d6c3a7475eeb8094a0f41d8199f10860c8aef09d2f1" dependencies = [ "num_enum", "serde", - "strum 0.26.2", + "strum", ] [[package]] @@ -1815,6 +1815,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.0.94" @@ -1866,7 +1875,7 @@ dependencies = [ "serde_json", "serial_test", "solang-parser", - "strum 0.26.2", + "strum", "tikv-jemallocator", "time", "tokio", @@ -2135,8 +2144,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ "crossterm", - "strum 0.26.2", - "strum_macros 0.26.2", + "strum", + "strum_macros", "unicode-width", ] @@ -2152,6 +2161,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -3042,7 +3064,7 @@ dependencies = [ "rlp", "serde", "serde_json", - "strum 0.26.2", + "strum", "syn 2.0.58", "tempfile", "thiserror", @@ -3476,7 +3498,7 @@ dependencies = [ "serde_json", "similar", "solang-parser", - "strum 0.26.2", + "strum", "svm-rs 0.4.1", "tempfile", "thiserror", @@ -3711,7 +3733,7 @@ dependencies = [ "regex", "serde", "strsim 0.10.0", - "strum 0.26.2", + "strum", "tempfile", "tokio", "tracing", @@ -6702,18 +6724,20 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.24.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425" +checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" dependencies = [ "bitflags 2.5.0", "cassowary", + "compact_str", "crossterm", "indoc", - "itertools 0.11.0", + "itertools 0.12.1", "lru", "paste", - "strum 0.25.0", + "stability", + "strum", "unicode-segmentation", "unicode-width", ] @@ -7830,6 +7854,16 @@ dependencies = [ "der", ] +[[package]] +name = "stability" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" +dependencies = [ + "quote", + "syn 2.0.58", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -7880,35 +7914,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros 0.25.3", -] - [[package]] name = "strum" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ - "strum_macros 0.26.2", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.58", + "strum_macros", ] [[package]] diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 3c1fe8480116..bc3c746e3d85 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -20,6 +20,6 @@ alloy-primitives.workspace = true crossterm = "0.27" eyre.workspace = true -ratatui = { version = "0.24.0", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.26", default-features = false, features = ["crossterm"] } revm.workspace = true tracing.workspace = true diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index b0da8a0c519f..157be4d3213d 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -5,13 +5,11 @@ use alloy_primitives::Address; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}; use foundry_evm_core::debug::{DebugNodeFlat, DebugStep}; use revm_inspectors::tracing::types::CallKind; -use std::{cell::RefCell, ops::ControlFlow}; +use std::ops::ControlFlow; /// This is currently used to remember last scroll position so screen doesn't wiggle as much. #[derive(Default)] pub(crate) struct DrawMemory { - // TODO - pub(crate) current_startline: RefCell, pub(crate) inner_call_index: usize, pub(crate) current_buf_startline: usize, pub(crate) current_stack_startline: usize, diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 8d91f2803f0c..656de5a2ab13 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -9,11 +9,11 @@ use ratatui::{ style::{Color, Modifier, Style}, terminal::Frame, text::{Line, Span, Text}, - widgets::{Block, Borders, Paragraph, Wrap}, + widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap}, }; use revm::interpreter::opcode; use revm_inspectors::tracing::types::CallKind; -use std::{cmp, collections::VecDeque, fmt::Write, io}; +use std::{collections::VecDeque, fmt::Write, io}; impl DebuggerContext<'_> { /// Draws the TUI layout and subcomponents to the given terminal. @@ -91,25 +91,25 @@ impl DebuggerContext<'_> { // constraints, so the `else` branch is unreachable. // Split off footer. - let [app, footer] = Layout::new() - .constraints([Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)]) - .direction(Direction::Vertical) - .split(area)[..] - else { + let [app, footer] = Layout::new( + Direction::Vertical, + [Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)], + ) + .split(area)[..] else { unreachable!() }; // Split the app in 4 vertically to construct all the panes. - let [op_pane, stack_pane, memory_pane, src_pane] = Layout::new() - .direction(Direction::Vertical) - .constraints([ + let [op_pane, stack_pane, memory_pane, src_pane] = Layout::new( + Direction::Vertical, + [ Constraint::Ratio(1, 6), Constraint::Ratio(1, 6), Constraint::Ratio(1, 6), Constraint::Ratio(3, 6), - ]) - .split(app)[..] - else { + ], + ) + .split(app)[..] else { unreachable!() }; @@ -138,37 +138,34 @@ impl DebuggerContext<'_> { let h_height = if self.show_shortcuts { 4 } else { 0 }; // Split off footer. - let [app, footer] = Layout::new() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)]) - .split(area)[..] - else { + let [app, footer] = Layout::new( + Direction::Vertical, + [Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)], + ) + .split(area)[..] else { unreachable!() }; // Split app in 2 horizontally. - let [app_left, app_right] = Layout::new() - .direction(Direction::Horizontal) - .constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]) - .split(app)[..] + let [app_left, app_right] = + Layout::new(Direction::Horizontal, [Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]) + .split(app)[..] else { unreachable!() }; // Split left pane in 2 vertically to opcode list and source. - let [op_pane, src_pane] = Layout::new() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) - .split(app_left)[..] + let [op_pane, src_pane] = + Layout::new(Direction::Vertical, [Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) + .split(app_left)[..] else { unreachable!() }; // Split right pane horizontally to construct stack and memory. - let [stack_pane, memory_pane] = Layout::new() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) - .split(app_right)[..] + let [stack_pane, memory_pane] = + Layout::new(Direction::Vertical, [Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) + .split(app_right)[..] else { unreachable!() }; @@ -380,63 +377,22 @@ impl DebuggerContext<'_> { } fn draw_op_list(&self, f: &mut Frame<'_>, area: Rect) { - let height = area.height as i32; - let extra_top_lines = height / 2; - // Absolute minimum start line - let abs_min_start = 0; - // Adjust for weird scrolling for max top line - let abs_max_start = (self.opcode_list.len() as i32 - 1) - (height / 2); - // actual minimum start line - let mut min_start = - cmp::max(self.current_step as i32 - height + extra_top_lines, abs_min_start) as usize; - - // actual max start line - let mut max_start = cmp::max( - cmp::min(self.current_step as i32 - extra_top_lines, abs_max_start), - abs_min_start, - ) as usize; - - // Sometimes, towards end of file, maximum and minim lines have swapped values. Swap if the - // case - if min_start > max_start { - std::mem::swap(&mut min_start, &mut max_start); - } - - let prev_start = *self.draw_memory.current_startline.borrow(); - let display_start = prev_start.clamp(min_start, max_start); - *self.draw_memory.current_startline.borrow_mut() = display_start; - - let max_pc = self.debug_steps().iter().map(|step| step.pc).max().unwrap_or(0); + let debug_steps = self.debug_steps(); + let max_pc = debug_steps.iter().map(|step| step.pc).max().unwrap_or(0); let max_pc_len = hex_digits(max_pc); - let debug_steps = self.debug_steps(); - let mut lines = Vec::new(); - let mut add_new_line = |line_number: usize| { - let mut line = String::with_capacity(64); - - let is_current_step = line_number == self.current_step; - if line_number < self.debug_steps().len() { - let step = &debug_steps[line_number]; - write!(line, "{:0>max_pc_len$x}|", step.pc).unwrap(); - line.push_str(if is_current_step { "▶" } else { " " }); - if let Some(op) = self.opcode_list.get(line_number) { - line.push_str(op); + let items = debug_steps + .iter() + .enumerate() + .map(|(i, step)| { + let mut content = String::with_capacity(64); + write!(content, "{:0>max_pc_len$x}|", step.pc).unwrap(); + if let Some(op) = self.opcode_list.get(i) { + content.push_str(op); } - } else { - line.push_str("END CALL"); - } - - let bg_color = if is_current_step { Color::DarkGray } else { Color::Reset }; - let style = Style::new().fg(Color::White).bg(bg_color); - lines.push(Line::from(Span::styled(line, style))); - }; - - for number in display_start..self.opcode_list.len() { - add_new_line(number); - } - - // Add one more "phantom" line so we see line where current segment execution ends - add_new_line(self.opcode_list.len()); + ListItem::new(Span::styled(content, Style::new().fg(Color::White))) + }) + .collect::>(); let title = format!( "Address: {} | PC: {} | Gas used in call: {}", @@ -445,8 +401,14 @@ impl DebuggerContext<'_> { self.current_step().total_gas_used, ); let block = Block::default().title(title).borders(Borders::ALL); - let paragraph = Paragraph::new(lines).block(block).wrap(Wrap { trim: true }); - f.render_widget(paragraph, area); + let hl_style = Style::new().fg(Color::White).bg(Color::DarkGray); + let list = List::new(items) + .block(block) + .highlight_symbol("▶") + .highlight_style(hl_style) + .scroll_padding(1); + let mut state = ListState::default().with_selected(Some(self.current_step)); + f.render_stateful_widget(list, area, &mut state); } fn draw_stack(&self, f: &mut Frame<'_>, area: Rect) { diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index 16964e7688b6..1fac3d051de2 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -115,16 +115,13 @@ impl Debugger { .spawn(move || Self::event_listener(tx)) .expect("failed to spawn thread"); - // Draw the initial state. - cx.draw(terminal)?; - // Start the event loop. loop { + cx.draw(terminal)?; match cx.handle_event(rx.recv()?) { ControlFlow::Continue(()) => {} ControlFlow::Break(reason) => return Ok(reason), } - cx.draw(terminal)?; } } From becee6ad41d8e4505baf084ab992081c913a6252 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 16 Apr 2024 00:35:08 +0200 Subject: [PATCH 2/3] refactor(debugger): event handlers --- crates/debugger/src/tui/context.rs | 270 ++++++++++++++--------------- 1 file changed, 131 insertions(+), 139 deletions(-) diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index 157be4d3213d..58a8e57f4275 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -118,6 +118,13 @@ impl<'a> DebuggerContext<'a> { self.opcode_list.extend(debug_steps.iter().map(DebugStep::pretty_opcode)); } + fn gen_opcode_list_if_necessary(&mut self) { + if self.last_index != self.draw_memory.inner_call_index { + self.gen_opcode_list(); + self.last_index = self.draw_memory.inner_call_index; + } + } + fn active_buffer(&self) -> &[u8] { match self.active_buffer { BufferKind::Memory => &self.current_step().memory, @@ -129,19 +136,18 @@ impl<'a> DebuggerContext<'a> { impl DebuggerContext<'_> { pub(crate) fn handle_event(&mut self, event: Event) -> ControlFlow { - if self.last_index != self.draw_memory.inner_call_index { - self.gen_opcode_list(); - self.last_index = self.draw_memory.inner_call_index; - } - - match event { + let ret = match event { Event::Key(event) => self.handle_key_event(event), Event::Mouse(event) => self.handle_mouse_event(event), _ => ControlFlow::Continue(()), - } + }; + // Generate the list after the event has been handled. + self.gen_opcode_list_if_necessary(); + ret } fn handle_key_event(&mut self, event: KeyEvent) -> ControlFlow { + // Breakpoints if let KeyCode::Char(c) = event.code { if c.is_alphabetic() && self.key_buffer.starts_with('\'') { self.handle_breakpoint(c); @@ -149,154 +155,130 @@ impl DebuggerContext<'_> { } } + let control = event.modifiers.contains(KeyModifiers::CONTROL); + match event.code { // Exit KeyCode::Char('q') => return ControlFlow::Break(ExitReason::CharExit), - // Move down - KeyCode::Char('j') | KeyCode::Down => { - // Grab number of times to do it - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - if event.modifiers.contains(KeyModifiers::CONTROL) { - let max_buf = (self.active_buffer().len() / 32).saturating_sub(1); - if self.draw_memory.current_buf_startline < max_buf { - self.draw_memory.current_buf_startline += 1; - } - } else if self.current_step < self.opcode_list.len() - 1 { - self.current_step += 1; - } else if self.draw_memory.inner_call_index < self.debug_arena().len() - 1 { - self.draw_memory.inner_call_index += 1; - self.current_step = 0; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('J') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - let max_stack = self.current_step().stack.len().saturating_sub(1); - if self.draw_memory.current_stack_startline < max_stack { - self.draw_memory.current_stack_startline += 1; - } + + // Scroll up the memory buffer + KeyCode::Char('k') | KeyCode::Up if control => self.repeat(|this| { + this.draw_memory.current_buf_startline = + this.draw_memory.current_buf_startline.saturating_sub(1); + }), + // Scroll down the memory buffer + KeyCode::Char('j') | KeyCode::Down if control => self.repeat(|this| { + let max_buf = (this.active_buffer().len() / 32).saturating_sub(1); + if this.draw_memory.current_buf_startline < max_buf { + this.draw_memory.current_buf_startline += 1; } - self.key_buffer.clear(); - } + }), + // Move up - KeyCode::Char('k') | KeyCode::Up => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - if event.modifiers.contains(KeyModifiers::CONTROL) { - self.draw_memory.current_buf_startline = - self.draw_memory.current_buf_startline.saturating_sub(1); - } else if self.current_step > 0 { - self.current_step -= 1; - } else if self.draw_memory.inner_call_index > 0 { - self.draw_memory.inner_call_index -= 1; - self.current_step = self.debug_steps().len() - 1; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('K') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - self.draw_memory.current_stack_startline = - self.draw_memory.current_stack_startline.saturating_sub(1); + KeyCode::Char('k') | KeyCode::Up => self.repeat(Self::step_back), + // Move down + KeyCode::Char('j') | KeyCode::Down => self.repeat(Self::step), + + // Scroll up the stack + KeyCode::Char('K') => self.repeat(|this| { + this.draw_memory.current_stack_startline = + this.draw_memory.current_stack_startline.saturating_sub(1); + }), + // Scroll down the stack + KeyCode::Char('J') => self.repeat(|this| { + let max_stack = this.current_step().stack.len().saturating_sub(1); + if this.draw_memory.current_stack_startline < max_stack { + this.draw_memory.current_stack_startline += 1; } - self.key_buffer.clear(); - } + }), + + // Cycle buffers KeyCode::Char('b') => { self.active_buffer = self.active_buffer.next(); self.draw_memory.current_buf_startline = 0; } + // Go to top of file KeyCode::Char('g') => { self.draw_memory.inner_call_index = 0; self.current_step = 0; - self.key_buffer.clear(); } + // Go to bottom of file KeyCode::Char('G') => { self.draw_memory.inner_call_index = self.debug_arena().len() - 1; - self.current_step = self.debug_steps().len() - 1; - self.key_buffer.clear(); + self.current_step = self.n_steps() - 1; } + // Go to previous call KeyCode::Char('c') => { self.draw_memory.inner_call_index = self.draw_memory.inner_call_index.saturating_sub(1); - self.current_step = self.debug_steps().len() - 1; - self.key_buffer.clear(); + self.current_step = self.n_steps() - 1; } + // Go to next call KeyCode::Char('C') => { if self.debug_arena().len() > self.draw_memory.inner_call_index + 1 { self.draw_memory.inner_call_index += 1; self.current_step = 0; } - self.key_buffer.clear(); } + // Step forward - KeyCode::Char('s') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - let remaining_ops = self.opcode_list[self.current_step..].to_vec(); - self.current_step += remaining_ops - .iter() - .enumerate() - .find_map(|(i, op)| { - if i < remaining_ops.len() - 1 { - match ( - op.contains("JUMP") && op != "JUMPDEST", - &*remaining_ops[i + 1], - ) { - (true, "JUMPDEST") => Some(i + 1), - _ => None, - } - } else { - None - } - }) - .unwrap_or(self.opcode_list.len() - 1); - if self.current_step > self.opcode_list.len() { - self.current_step = self.opcode_list.len() - 1 - }; + KeyCode::Char('s') => self.repeat(|this| { + let remaining_ops = &this.opcode_list[this.current_step..]; + if let Some((i, _)) = remaining_ops.iter().enumerate().skip(1).find(|&(i, op)| { + let prev = &remaining_ops[i - 1]; + let prev_is_jump = prev.contains("JUMP") && prev != "JUMPDEST"; + let is_jumpdest = op == "JUMPDEST"; + prev_is_jump && is_jumpdest + }) { + this.current_step += i; } - self.key_buffer.clear(); - } + }), + // Step backwards - KeyCode::Char('a') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - let prev_ops = self.opcode_list[..self.current_step].to_vec(); - self.current_step = prev_ops - .iter() - .enumerate() - .rev() - .find_map(|(i, op)| { - if i > 0 { - match ( - prev_ops[i - 1].contains("JUMP") && - prev_ops[i - 1] != "JUMPDEST", - &**op, - ) { - (true, "JUMPDEST") => Some(i - 1), - _ => None, - } - } else { - None - } - }) - .unwrap_or_default(); - } - self.key_buffer.clear(); - } - // toggle stack labels + KeyCode::Char('a') => self.repeat(|this| { + let ops = &this.opcode_list[..this.current_step]; + this.current_step = ops + .iter() + .enumerate() + .skip(1) + .rev() + .find(|&(i, op)| { + let prev = &ops[i - 1]; + let prev_is_jump = prev.contains("JUMP") && prev != "JUMPDEST"; + let is_jumpdest = op == "JUMPDEST"; + prev_is_jump && is_jumpdest + }) + .map(|(i, _)| i) + .unwrap_or_default(); + }), + + // Toggle stack labels KeyCode::Char('t') => self.stack_labels = !self.stack_labels, - // toggle memory utf8 decoding + + // Toggle memory UTF-8 decoding KeyCode::Char('m') => self.buf_utf = !self.buf_utf, - // toggle help notice + + // Toggle help notice KeyCode::Char('h') => self.show_shortcuts = !self.show_shortcuts, + + // Numbers for repeating commands or breakpoints KeyCode::Char( other @ ('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '\''), - ) => self.key_buffer.push(other), - _ => self.key_buffer.clear(), + ) => { + // Early return to not clear the buffer. + self.key_buffer.push(other); + return ControlFlow::Continue(()); + } + + // Unknown/unhandled key code + _ => {} }; + self.key_buffer.clear(); ControlFlow::Continue(()) } @@ -319,37 +301,47 @@ impl DebuggerContext<'_> { fn handle_mouse_event(&mut self, event: MouseEvent) -> ControlFlow { match event.kind { - MouseEventKind::ScrollUp => { - if self.current_step > 0 { - self.current_step -= 1; - } else if self.draw_memory.inner_call_index > 0 { - self.draw_memory.inner_call_index -= 1; - self.draw_memory.current_buf_startline = 0; - self.draw_memory.current_stack_startline = 0; - self.current_step = self.debug_steps().len() - 1; - } - } - MouseEventKind::ScrollDown => { - if self.current_step < self.opcode_list.len() - 1 { - self.current_step += 1; - } else if self.draw_memory.inner_call_index < self.debug_arena().len() - 1 { - self.draw_memory.inner_call_index += 1; - self.draw_memory.current_buf_startline = 0; - self.draw_memory.current_stack_startline = 0; - self.current_step = 0; - } - } + MouseEventKind::ScrollUp => self.step_back(), + MouseEventKind::ScrollDown => self.step(), _ => {} } ControlFlow::Continue(()) } + + fn step_back(&mut self) { + if self.current_step > 0 { + self.current_step -= 1; + } else if self.draw_memory.inner_call_index > 0 { + self.draw_memory.inner_call_index -= 1; + self.current_step = self.n_steps() - 1; + } + } + + fn step(&mut self) { + if self.current_step < self.n_steps() - 1 { + self.current_step += 1; + } else if self.draw_memory.inner_call_index < self.debug_arena().len() - 1 { + self.draw_memory.inner_call_index += 1; + self.current_step = 0; + } + } + + /// Calls a closure `f` the number of times specified in the key buffer, and at least once. + fn repeat(&mut self, mut f: impl FnMut(&mut Self)) { + for _ in 0..buffer_as_number(&self.key_buffer) { + f(self); + } + } + + fn n_steps(&self) -> usize { + self.debug_steps().len() + } } /// Grab number from buffer. Used for something like '10k' to move up 10 operations -fn buffer_as_number(s: &str, default_value: usize) -> usize { - match s.parse() { - Ok(num) if num >= 1 => num, - _ => default_value, - } +fn buffer_as_number(s: &str) -> usize { + const MIN: usize = 1; + const MAX: usize = 100_000; + s.parse().unwrap_or(MIN).clamp(MIN, MAX) } From befe22ec7def2e832e049a906e50dc5336d785be Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 16 Apr 2024 01:06:00 +0200 Subject: [PATCH 3/3] fmt --- crates/debugger/src/tui/draw.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 656de5a2ab13..b80a8a77a107 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -401,11 +401,10 @@ impl DebuggerContext<'_> { self.current_step().total_gas_used, ); let block = Block::default().title(title).borders(Borders::ALL); - let hl_style = Style::new().fg(Color::White).bg(Color::DarkGray); let list = List::new(items) .block(block) .highlight_symbol("▶") - .highlight_style(hl_style) + .highlight_style(Style::new().fg(Color::White).bg(Color::DarkGray)) .scroll_padding(1); let mut state = ListState::default().with_selected(Some(self.current_step)); f.render_stateful_widget(list, area, &mut state);