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"
|
libc = "0.2.47"
|
||||||
term_size = { version = "0.3.1", optional = true }
|
term_size = { version = "0.3.1", optional = true }
|
||||||
crossbeam-channel = "0.3.6"
|
crossbeam-channel = "0.3.6"
|
||||||
|
lazy_static = "1.2.0"
|
||||||
|
|
||||||
[dependencies.num]
|
[dependencies.num]
|
||||||
default-features = false
|
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)
|
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 a sink for asynchronous callbacks.
|
||||||
///
|
///
|
||||||
/// Returns the sender part of a channel, that allows to send
|
/// Returns the sender part of a channel, that allows to send
|
||||||
|
@ -69,6 +69,8 @@ extern crate enumset;
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate crossbeam_channel;
|
extern crate crossbeam_channel;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
|
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -108,6 +110,7 @@ pub mod utils;
|
|||||||
|
|
||||||
pub mod align;
|
pub mod align;
|
||||||
pub mod direction;
|
pub mod direction;
|
||||||
|
pub mod logger;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
pub mod theme;
|
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 canvas;
|
||||||
mod checkbox;
|
mod checkbox;
|
||||||
mod circular_focus;
|
mod circular_focus;
|
||||||
|
mod debug_view;
|
||||||
mod dialog;
|
mod dialog;
|
||||||
mod dummy;
|
mod dummy;
|
||||||
mod edit_view;
|
mod edit_view;
|
||||||
@ -72,6 +73,7 @@ pub use self::button::Button;
|
|||||||
pub use self::canvas::Canvas;
|
pub use self::canvas::Canvas;
|
||||||
pub use self::checkbox::Checkbox;
|
pub use self::checkbox::Checkbox;
|
||||||
pub use self::circular_focus::CircularFocus;
|
pub use self::circular_focus::CircularFocus;
|
||||||
|
pub use self::debug_view::DebugView;
|
||||||
pub use self::dialog::{Dialog, DialogFocus};
|
pub use self::dialog::{Dialog, DialogFocus};
|
||||||
pub use self::dummy::DummyView;
|
pub use self::dummy::DummyView;
|
||||||
pub use self::edit_view::EditView;
|
pub use self::edit_view::EditView;
|
||||||
|
Loading…
Reference in New Issue
Block a user