mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-09 19:00:46 +00:00
Add CursiveLogger and DebugView
This commit is contained in:
parent
4852d25f51
commit
8b3f5a8de7
@ -29,6 +29,7 @@ xi-unicode = "0.1.0"
|
||||
libc = "0.2.47"
|
||||
term_size = { version = "0.3.1", optional = true }
|
||||
crossbeam-channel = "0.3.6"
|
||||
lazy_static = "1.2.0"
|
||||
|
||||
[dependencies.num]
|
||||
default-features = false
|
||||
|
17
examples/logger.rs
Normal file
17
examples/logger.rs
Normal file
@ -0,0 +1,17 @@
|
||||
extern crate cursive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
fn main() {
|
||||
cursive::logger::init();
|
||||
debug!("Starting!");
|
||||
|
||||
let mut siv = cursive::Cursive::default();
|
||||
siv.add_global_callback('q', cursive::Cursive::quit);
|
||||
siv.add_global_callback('~', cursive::Cursive::toggle_debug_view);
|
||||
siv.add_global_callback('l', |_| debug!("Wooo"));
|
||||
error!("BAD!!!");
|
||||
|
||||
siv.run();
|
||||
}
|
@ -182,6 +182,24 @@ impl Cursive {
|
||||
Self::new(backend::dummy::Backend::init)
|
||||
}
|
||||
|
||||
/// Show the debug view, or hide it if it's already visible.
|
||||
pub fn toggle_debug_view(&mut self) {
|
||||
static DEBUG_VIEW_ID: &'static str = "_cursive_debug_view";
|
||||
|
||||
let stack = self.screen_mut();
|
||||
if let Some(pos) = stack.find_layer_from_id(DEBUG_VIEW_ID) {
|
||||
info!("Foundit");
|
||||
stack.remove_layer(pos);
|
||||
} else {
|
||||
stack.add_layer(
|
||||
views::Dialog::around(views::ScrollView::new(
|
||||
views::IdView::new(DEBUG_VIEW_ID, views::DebugView::new()),
|
||||
))
|
||||
.title("Debug console"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a sink for asynchronous callbacks.
|
||||
///
|
||||
/// Returns the sender part of a channel, that allows to send
|
||||
|
@ -69,6 +69,8 @@ extern crate enumset;
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate crossbeam_channel;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
|
||||
#[macro_use]
|
||||
@ -108,6 +110,7 @@ pub mod utils;
|
||||
|
||||
pub mod align;
|
||||
pub mod direction;
|
||||
pub mod logger;
|
||||
pub mod menu;
|
||||
pub mod rect;
|
||||
pub mod theme;
|
||||
|
50
src/logger.rs
Normal file
50
src/logger.rs
Normal file
@ -0,0 +1,50 @@
|
||||
//! Logging utilities
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Saves all log records in a global deque.
|
||||
///
|
||||
/// Uses a `DebugView` to access it.
|
||||
struct CursiveLogger;
|
||||
|
||||
static LOGGER: CursiveLogger = CursiveLogger;
|
||||
|
||||
lazy_static! {
|
||||
/// Circular buffer for logs. Use it to implement `DebugView`.
|
||||
pub static ref LOGS: Mutex<VecDeque<(log::Level, String)>> =
|
||||
Mutex::new(VecDeque::new());
|
||||
}
|
||||
|
||||
impl log::Log for CursiveLogger {
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
let mut logs = LOGS.lock().unwrap();
|
||||
// TODO: customize the format? Use colors? Save more info?
|
||||
if logs.len() == logs.capacity() {
|
||||
logs.pop_front();
|
||||
}
|
||||
logs.push_back((record.level(), format!("{}", record.args())));
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
/// Initialize the Cursive logger.
|
||||
///
|
||||
/// Make sure this is the only logger your are using.
|
||||
///
|
||||
/// Use a `DebugView` to see the logs.
|
||||
pub fn init() {
|
||||
// TODO: Configure the deque size?
|
||||
LOGS.lock().unwrap().reserve(1_000);
|
||||
|
||||
// This will panic if `set_logger` was already called.
|
||||
log::set_logger(&LOGGER).unwrap();
|
||||
|
||||
// TODO: read the level from env variable? From argument?
|
||||
log::set_max_level(log::LevelFilter::Trace);
|
||||
}
|
50
src/views/debug_view.rs
Normal file
50
src/views/debug_view.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use logger;
|
||||
use vec::Vec2;
|
||||
use view::View;
|
||||
use Printer;
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// View used for debugging, showing logs.
|
||||
pub struct DebugView {
|
||||
// We'll want to store the formatting (line split)
|
||||
// ... or 1 line per log?
|
||||
}
|
||||
|
||||
impl DebugView {
|
||||
/// Creates a new DebugView.
|
||||
pub fn new() -> Self {
|
||||
DebugView {}
|
||||
}
|
||||
}
|
||||
|
||||
impl View for DebugView {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
let logs = logger::LOGS.lock().unwrap();
|
||||
// Only print the last logs, so skip what doesn't fit
|
||||
let skipped = logs.len().saturating_sub(printer.size.y);
|
||||
|
||||
for (i, &(level, ref text)) in logs.iter().skip(skipped).enumerate() {
|
||||
printer.print((0, i), &format!("[{}] {}", level, text));
|
||||
}
|
||||
}
|
||||
|
||||
fn required_size(&mut self, _constraint: Vec2) -> Vec2 {
|
||||
// TODO: read the logs, and compute the required size to print it.
|
||||
let logs = logger::LOGS.lock().unwrap();
|
||||
|
||||
// The longest line sets the width
|
||||
let w = logs
|
||||
.iter()
|
||||
.map(|&(_, ref content)| content.width() + "[ERROR] ".width())
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
let h = logs.len();
|
||||
|
||||
Vec2::new(w, h)
|
||||
}
|
||||
|
||||
fn layout(&mut self, _size: Vec2) {
|
||||
// Uh?
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ mod button;
|
||||
mod canvas;
|
||||
mod checkbox;
|
||||
mod circular_focus;
|
||||
mod debug_view;
|
||||
mod dialog;
|
||||
mod dummy;
|
||||
mod edit_view;
|
||||
@ -72,6 +73,7 @@ pub use self::button::Button;
|
||||
pub use self::canvas::Canvas;
|
||||
pub use self::checkbox::Checkbox;
|
||||
pub use self::circular_focus::CircularFocus;
|
||||
pub use self::debug_view::DebugView;
|
||||
pub use self::dialog::{Dialog, DialogFocus};
|
||||
pub use self::dummy::DummyView;
|
||||
pub use self::edit_view::EditView;
|
||||
|
Loading…
Reference in New Issue
Block a user