Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91 lines
2.7 KiB
Rust
91 lines
2.7 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::fmt;
|
|
|
|
use crate::compiler::tokens::Span;
|
|
use crate::error::ErrorKind;
|
|
use crate::value::Value;
|
|
|
|
/// This is a snapshot of the debug information.
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "debug")))]
|
|
#[derive(Default)]
|
|
pub(crate) struct DebugInfo {
|
|
pub(crate) template_source: Option<String>,
|
|
pub(crate) referenced_locals: BTreeMap<String, Value>,
|
|
}
|
|
|
|
struct VarPrinter<'x>(&'x BTreeMap<String, Value>);
|
|
|
|
impl fmt::Debug for VarPrinter<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if self.0.is_empty() {
|
|
return f.write_str("No referenced variables");
|
|
}
|
|
let mut m = f.debug_struct("Referenced variables:");
|
|
let mut vars = self.0.iter().collect::<Vec<_>>();
|
|
vars.sort_by_key(|x| x.0);
|
|
for (key, value) in vars {
|
|
m.field(key, value);
|
|
}
|
|
m.finish()
|
|
}
|
|
}
|
|
|
|
impl DebugInfo {
|
|
pub fn source(&self) -> Option<&str> {
|
|
self.template_source.as_deref()
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_debug_info(
|
|
f: &mut fmt::Formatter,
|
|
name: Option<&str>,
|
|
kind: ErrorKind,
|
|
line: Option<usize>,
|
|
span: Option<Span>,
|
|
info: &DebugInfo,
|
|
) -> fmt::Result {
|
|
if let Some(source) = info.source() {
|
|
let title = format!(
|
|
" {} ",
|
|
name.unwrap_or_default()
|
|
.rsplit(&['/', '\\'])
|
|
.next()
|
|
.unwrap_or("Template Source")
|
|
);
|
|
ok!(writeln!(f));
|
|
writeln!(f, "{:-^1$}", title, 79).unwrap();
|
|
let lines: Vec<_> = source.lines().enumerate().collect();
|
|
let idx = line.unwrap_or(1).saturating_sub(1);
|
|
let skip = idx.saturating_sub(3);
|
|
let pre = lines.iter().skip(skip).take(3.min(idx)).collect::<Vec<_>>();
|
|
let post = lines.iter().skip(idx + 1).take(3).collect::<Vec<_>>();
|
|
for (idx, line) in pre {
|
|
writeln!(f, "{:>4} | {}", idx + 1, line).unwrap();
|
|
}
|
|
|
|
if let Some(line) = lines.get(idx) {
|
|
writeln!(f, "{:>4} > {}", idx + 1, line.1).unwrap();
|
|
}
|
|
if let Some(span) = span {
|
|
if span.start_line == span.end_line {
|
|
ok!(writeln!(
|
|
f,
|
|
" i {}{} {}",
|
|
" ".repeat(span.start_col as usize),
|
|
"^".repeat(span.end_col as usize - span.start_col as usize),
|
|
kind,
|
|
));
|
|
}
|
|
}
|
|
|
|
for (idx, line) in post {
|
|
writeln!(f, "{:>4} | {}", idx + 1, line).unwrap();
|
|
}
|
|
write!(f, "{:~^1$}", "", 79).unwrap();
|
|
}
|
|
ok!(writeln!(f));
|
|
ok!(writeln!(f, "{:#?}", VarPrinter(&info.referenced_locals)));
|
|
write!(f, "{:-^1$}", "", 79).unwrap();
|
|
Ok(())
|
|
}
|