Skip to content

Commit 09f653e

Browse files
Merge pull request #134 from wcampbell0x2a/add-completions-handle
2 parents 18d554a + 572b4cb commit 09f653e

File tree

5 files changed

+82
-10
lines changed

5 files changed

+82
-10
lines changed

src/gdb.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use log::{debug, error, info, trace};
1010
use crate::deref::Deref;
1111
use crate::mi::{
1212
data_disassemble, data_disassemble_pc, data_read_memory_bytes, data_read_sp_bytes,
13-
join_registers, match_inner_items, parse_asm_insns_values, parse_key_value_pairs,
14-
parse_memory_mappings_new, parse_memory_mappings_old, parse_mi_response,
13+
join_registers, match_inner_items, normalize_value, parse_asm_insns_values,
14+
parse_key_value_pairs, parse_memory_mappings_new, parse_memory_mappings_old, parse_mi_response,
1515
parse_register_names_values, parse_register_values, read_pc_value, Asm, MIResponse, Mapping,
1616
MemoryMapping, INSTRUCTION_LEN, MEMORY_MAP_START_STR_NEW, MEMORY_MAP_START_STR_OLD,
1717
};
@@ -38,6 +38,7 @@ pub fn gdb_interact(
3838
hexdump_arc: Arc<Mutex<Option<(u64, Vec<u8>)>>>,
3939
async_result_arc: Arc<Mutex<String>>,
4040
bt: Arc<Mutex<Vec<Bt>>>,
41+
completions: Arc<Mutex<Vec<String>>>,
4142
) {
4243
let mut current_map = (None, String::new());
4344

@@ -93,6 +94,18 @@ pub fn gdb_interact(
9394
}
9495
bts.push(bt);
9596
}
97+
} else if kv.contains_key("matches") {
98+
let mut completions = completions.lock().unwrap();
99+
completions.clear();
100+
let matches = &kv["matches"];
101+
let m_str = matches.strip_prefix(r#"["#).unwrap();
102+
let m_str = m_str.strip_suffix(r#"]"#).unwrap();
103+
let data = parse_key_value_pairs(m_str);
104+
105+
for (k, _) in data {
106+
let k: String = k.chars().filter(|&c| c != '\"').collect();
107+
completions.push(k);
108+
}
96109
}
97110
} else if status == "error" {
98111
// assume this is from us, pop off an unexpected

src/main.rs

+32
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ struct App {
194194
/// Left side of status in TUI
195195
status: Arc<Mutex<String>>,
196196
bt: Arc<Mutex<Vec<Bt>>>,
197+
completions: Arc<Mutex<Vec<String>>>,
197198
}
198199

199200
impl App {
@@ -264,6 +265,7 @@ impl App {
264265
async_result: Arc::new(Mutex::new(String::new())),
265266
status: Arc::new(Mutex::new(String::new())),
266267
bt: Arc::new(Mutex::new(vec![])),
268+
completions: Arc::new(Mutex::new(vec![])),
267269
};
268270

269271
(reader, app)
@@ -425,6 +427,7 @@ fn into_gdb(app: &App, gdb_stdout: BufReader<Box<dyn Read + Send>>) {
425427
let hexdump_arc = Arc::clone(&app.hexdump);
426428
let async_result_arc = Arc::clone(&app.async_result);
427429
let bt_arc = Arc::clone(&app.bt);
430+
let completions_arc = Arc::clone(&app.completions);
428431

429432
// Thread to read GDB output and parse it
430433
thread::spawn(move || {
@@ -447,6 +450,7 @@ fn into_gdb(app: &App, gdb_stdout: BufReader<Box<dyn Read + Send>>) {
447450
hexdump_arc,
448451
async_result_arc,
449452
bt_arc,
453+
completions_arc,
450454
)
451455
});
452456
}
@@ -466,6 +470,21 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<
466470
}
467471
}
468472

473+
// check if completions are back and we need to replace the input
474+
{
475+
let mut completions = app.completions.lock().unwrap();
476+
if !completions.is_empty() {
477+
// Just replace if completions is 1
478+
if completions.len() == 1 {
479+
app.input = Input::new(completions[0].clone());
480+
// we are done with the values, clear them
481+
completions.clear();
482+
}
483+
484+
// if else, we display them
485+
}
486+
}
487+
469488
if crossterm::event::poll(Duration::from_millis(10))? {
470489
if let Event::Key(key) = event::read()? {
471490
if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
@@ -682,6 +701,9 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<
682701
(InputMode::Normal, KeyCode::Char('K'), Mode::OnlyHexdump) => {
683702
scroll_up(50, &mut app.hexdump_scroll, &mut app.hexdump_scroll_state);
684703
}
704+
(_, KeyCode::Tab, _) => {
705+
completion(app)?;
706+
}
685707
(_, KeyCode::Enter, _) => {
686708
key_enter(app)?;
687709
}
@@ -692,6 +714,7 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<
692714
key_up(app);
693715
}
694716
(InputMode::Editing, _, _) => {
717+
app.completions.lock().unwrap().clear();
695718
app.input.handle_event(&Event::Key(key));
696719
}
697720
_ => (),
@@ -742,6 +765,15 @@ fn key_down(app: &mut App) {
742765
}
743766
}
744767

768+
fn completion(app: &mut App) -> Result<(), io::Error> {
769+
let val = app.input.clone();
770+
let val = val.value();
771+
let cmd = format!("-complete \"{val}\"");
772+
gdb::write_mi(&app.gdb_stdin, &cmd);
773+
774+
Ok(())
775+
}
776+
745777
fn key_enter(app: &mut App) -> Result<(), io::Error> {
746778
if app.input.value().is_empty() {
747779
app.sent_input.offset = 0;

src/mi.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ pub struct Asm {
169169
}
170170

171171
/// Normalizes a value: trims quotes around strings like "\"0\"" -> "0"
172-
fn normalize_value(value: &str) -> String {
172+
pub fn normalize_value(value: &str) -> String {
173173
let trimmed = value.trim();
174174
if trimmed.starts_with('"') && trimmed.ends_with('"') {
175175
trimmed[1..trimmed.len() - 1].to_string() // Remove surrounding quotes

src/ui/input.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use ratatui::layout::Constraint::{Fill, Length, Min};
2+
use ratatui::layout::Layout;
13
use ratatui::prelude::Stylize;
24
use ratatui::widgets::{Block, Borders, Paragraph};
35
use ratatui::{layout::Rect, style::Style, Frame};
@@ -8,7 +10,7 @@ use crate::{App, InputMode};
810

911
pub fn draw_input(title_area: Rect, app: &App, f: &mut Frame, input: Rect) {
1012
// Input
11-
let width = title_area.width.max(3) - 3;
13+
let width = title_area.width - 3;
1214
// keep 2 for borders and 1 for cursor
1315

1416
let scroll = app.input.visual_scroll(width as usize);

src/ui/mod.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use log::trace;
12
use ratatui::layout::Constraint::{Fill, Length, Min};
23
use ratatui::layout::Layout;
34
use ratatui::style::Color;
45
use ratatui::style::Style;
56
use ratatui::text::Span;
7+
use ratatui::widgets::Paragraph;
68
use ratatui::Frame;
79

810
use crate::deref::Deref;
@@ -49,8 +51,16 @@ pub fn ui(f: &mut Frame, app: &mut App) {
4951
// If only output, then no top and fill all with output
5052
if let Mode::OnlyOutput = app.mode {
5153
let output_size = Fill(1);
52-
let vertical = Layout::vertical([Length(2), output_size, Length(3)]);
53-
let [title_area, output, input] = vertical.areas(f.area());
54+
let completions = (!app.completions.lock().unwrap().is_empty()) as u16;
55+
let vertical = Layout::vertical([Length(2), output_size, Length(3), Length(completions)]);
56+
let [title_area, output, input, completions_area] = vertical.areas(f.area());
57+
58+
// Add completions if any are found
59+
let completions = app.completions.lock().unwrap().join(" ");
60+
if completions_area.area() != 0 {
61+
let completions_str = Paragraph::new(completions);
62+
f.render_widget(completions_str, completions_area);
63+
}
5464

5565
title::draw_title_area(app, f, title_area);
5666
output::draw_output(app, f, output, true);
@@ -62,10 +72,18 @@ pub fn ui(f: &mut Frame, app: &mut App) {
6272
let output_size = Length(SAVED_OUTPUT as u16);
6373

6474
let bt_len = app.bt.lock().unwrap().len();
75+
let completions = (!app.completions.lock().unwrap().is_empty()) as u16;
6576
let top = if bt_len == 0 {
66-
let vertical = Layout::vertical([Length(2), top_size, output_size, Length(3)]);
67-
let [title_area, top, output, input] = vertical.areas(f.area());
68-
77+
let vertical =
78+
Layout::vertical([Length(2), top_size, output_size, Length(3), Length(completions)]);
79+
let [title_area, top, output, input, completions_area] = vertical.areas(f.area());
80+
81+
// Add completions if any are found
82+
let completions = app.completions.lock().unwrap().join(" ");
83+
if completions_area.area() != 0 {
84+
let completions_str = Paragraph::new(completions);
85+
f.render_widget(completions_str, completions_area);
86+
}
6987
title::draw_title_area(app, f, title_area);
7088
output::draw_output(app, f, output, false);
7189
input::draw_input(title_area, app, f, input);
@@ -78,9 +96,16 @@ pub fn ui(f: &mut Frame, app: &mut App) {
7896
Length(bt_len as u16 + 1),
7997
output_size,
8098
Length(3),
99+
Length(completions),
81100
]);
82-
let [title_area, top, bt_area, output, input] = vertical.areas(f.area());
101+
let [title_area, top, bt_area, output, input, completions_area] = vertical.areas(f.area());
83102

103+
// Add completions if any are found
104+
let completions = app.completions.lock().unwrap().join(" ");
105+
if completions_area.area() != 0 {
106+
let completions_str = Paragraph::new(completions);
107+
f.render_widget(completions_str, completions_area);
108+
}
84109
bt::draw_bt(app, f, bt_area);
85110
title::draw_title_area(app, f, title_area);
86111
output::draw_output(app, f, output, false);

0 commit comments

Comments
 (0)