Skip to content

Commit 02abfab

Browse files
authored
Inline Value Display (#23)
* Analyzer * Analyze * Show overlay * Update * My god it's full of stars * Update test * Properly render types * Only show the current line * Support rendering resources * Test for resource
1 parent 0f3dd61 commit 02abfab

File tree

10 files changed

+683
-70
lines changed

10 files changed

+683
-70
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
2+
/debug.log
23
/test.log

Cargo.lock

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name = "debug-tui"
33
version = "0.1.0"
44
edition = "2021"
55

6+
[build-dependencies]
7+
cc="*"
68
[dependencies]
79
anyhow = "1.0.97"
810
base64 = "0.22.1"
@@ -15,6 +17,8 @@ ratatui = "0.29.0"
1517
serde = { version = "1.0.219", features = ["derive"] }
1618
simple-logging = "2.0.2"
1719
tokio = { version = "1.44.1", features = ["full"] }
20+
tree-sitter = "0.25.3"
21+
tree-sitter-php = "0.23.11"
1822
tui-input = "0.11.1"
1923
xmlem = "0.3.3"
2024
xmltree = "0.11.0"

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Interactive XDebug step debugger for your terminal with vim-like key bindings.
99
- **Travel backwards**: it's not quite time travel, but you can revisit
1010
previous steps in _history mode_.
1111
- **Vim-like motions**: Typing `100n` will repeat "step into" 100 times.
12+
- **Inline values**: Show variable values inline with the source code.
1213

1314
## Installation
1415

@@ -33,6 +34,8 @@ Prefix with number to repeat:
3334
- `J` scroll down 10
3435
- `k` scroll up
3536
- `K` scroll up 10
37+
- `+` increase context depth
38+
- `-` decrease context depth
3639
- `tab` switch pane
3740
- `enter` toggle pane focus (full screen)
3841
- `?` Show help

src/analyzer.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use std::collections::HashMap;
2+
3+
use anyhow::Result;
4+
use tree_sitter::{Node, Parser, Tree};
5+
6+
#[derive(Clone, Debug, Default, PartialEq)]
7+
pub struct Value {
8+
pub value: String,
9+
}
10+
11+
#[derive(Clone, Debug, PartialEq)]
12+
pub struct Position {
13+
pub row: usize,
14+
pub char: usize,
15+
}
16+
17+
impl Position {
18+
pub fn new(row: usize, column: usize) -> Self {
19+
Self { row, char: column }
20+
}
21+
}
22+
23+
#[derive(Clone, Debug, PartialEq)]
24+
pub struct Range {
25+
pub start: Position,
26+
pub end: Position,
27+
}
28+
29+
impl Range {
30+
31+
pub fn new(start: Position, end: Position) -> Self {
32+
Range { start, end }
33+
}
34+
}
35+
36+
#[derive(Clone, Debug, PartialEq)]
37+
pub struct VariableRef {
38+
pub range: Range,
39+
pub name: String,
40+
pub value: Option<Value>,
41+
}
42+
43+
// variable's start char is the key
44+
type Row = HashMap<usize, VariableRef>;
45+
46+
#[derive(Clone, Debug)]
47+
pub struct Analysis {
48+
rows: HashMap<usize,Row>,
49+
}
50+
51+
impl Analysis {
52+
fn register(&mut self, variable: VariableRef) {
53+
let line = self.rows.entry(variable.range.end.row).or_default();
54+
line.insert(variable.range.start.char, variable);
55+
}
56+
57+
pub fn row(&self, number: usize) -> Row {
58+
let value = self.rows.get(&number);
59+
if value.is_none() {
60+
return Row::new();
61+
}
62+
value.unwrap().clone()
63+
}
64+
65+
fn new() -> Self {
66+
Self{
67+
rows: HashMap::new(),
68+
}
69+
}
70+
}
71+
72+
pub struct Analyser {
73+
analysis: Analysis,
74+
}
75+
76+
impl Default for Analyser {
77+
fn default() -> Self {
78+
Self::new()
79+
}
80+
}
81+
82+
impl Analyser {
83+
pub fn analyze(&mut self, source: &str) -> Result<Analysis> {
84+
self.analysis = Analysis::new();
85+
let tree = self.parse(source);
86+
self.walk(&tree.root_node(), source);
87+
88+
Ok(self.analysis.clone())
89+
}
90+
91+
fn parse(&mut self, source: &str) -> Tree{
92+
let mut parser = Parser::new();
93+
let language = tree_sitter_php::LANGUAGE_PHP;
94+
parser.set_language(&language.into()).unwrap();
95+
96+
parser.parse(source, None).unwrap()
97+
98+
}
99+
100+
fn walk(&mut self, node: &Node, source: &str) {
101+
let count = node.child_count();
102+
if node.kind() == "variable_name" {
103+
let var_ref = VariableRef{
104+
name: node.utf8_text(source.as_bytes()).unwrap().to_string(),
105+
range: Range{
106+
start: Position {row: node.start_position().row, char: node.start_position().column},
107+
end: Position {row: node.end_position().row, char: node.end_position().column},
108+
},
109+
value: None,
110+
};
111+
self.analysis.register(var_ref);
112+
}
113+
114+
for index in 0..count {
115+
let child = node.child(index).unwrap();
116+
self.walk(&child, source);
117+
}
118+
}
119+
120+
pub fn new() -> Self {
121+
Self { analysis: Analysis { rows: HashMap::new() } }
122+
}
123+
}
124+
125+
#[cfg(test)]
126+
mod test {
127+
use super::*;
128+
use pretty_assertions::assert_eq;
129+
130+
#[test]
131+
fn test_analyse_vars() -> Result<(), anyhow::Error> {
132+
let source = r#"<?php
133+
$var1 = 'hello'; $var2 = 'bar';
134+
"#;
135+
let analysis = Analyser::new().analyze(source)?;
136+
let line = analysis.row(1);
137+
assert_eq!(2, line.values().len());
138+
139+
assert_eq!(&VariableRef{
140+
range: Range::new(Position::new(1,0), Position::new(1,5)),
141+
name: "$var1".to_string(),
142+
value: None,
143+
}, line.get(&0).unwrap());
144+
145+
assert_eq!(&VariableRef{
146+
range: Range::new(Position::new(1,17), Position::new(1,22)),
147+
name: "$var2".to_string(),
148+
value: None,
149+
}, line.get(&17).unwrap());
150+
Ok(())
151+
}
152+
153+
#[test]
154+
fn test_analyse_list() -> Result<(), anyhow::Error> {
155+
let source = r#"<?php
156+
list($var1, $var2) = some_call();
157+
"#;
158+
let analysis = Analyser::new().analyze(source)?;
159+
let line = analysis.row(1);
160+
assert_eq!(2, line.values().len());
161+
162+
assert_eq!(&VariableRef{
163+
range: Range::new(Position::new(1,5), Position::new(1,10)),
164+
name: "$var1".to_string(),
165+
value: None,
166+
}, line.get(&5).unwrap());
167+
Ok(())
168+
}
169+
}
170+

0 commit comments

Comments
 (0)