use bevy::prelude::*;
use ratatui::{
prelude::{Rect, *},
widgets::Block,
};
use crate::backend::log::LogRevsetEvent;
use super::change_buffer::{ChangeBufferSelectionEvent, RevisionSelection};
use super::prelude::*;
#[mutants::skip]
#[tracing::instrument(skip_all)]
pub fn plugin(app: &mut App) {
trace!("Initializing plugin...");
app.add_systems(
Update,
(monitor_logged_revset, monitor_selected_revset).in_set(AppSet::Update),
);
trace!("Plugin initialized.");
}
#[derive(Component, Default)]
pub struct StatusLine {
pub logged_revset: Option<String>,
pub selected_revset: Option<RevisionSelection>,
}
fn monitor_logged_revset(
mut ev_log_request: EventReader<LogRevsetEvent>,
mut status_line: Query<&mut StatusLine>,
) {
let Ok(mut status_line) = status_line.get_single_mut() else {
return;
};
for LogRevsetEvent(revset) in ev_log_request.read() {
status_line.logged_revset = Some(revset.clone());
}
}
fn monitor_selected_revset(
mut ev_selection: EventReader<ChangeBufferSelectionEvent>,
mut status_line: Query<&mut StatusLine>,
) {
let Ok(mut status_line) = status_line.get_single_mut() else {
return;
};
for ChangeBufferSelectionEvent(selection) in ev_selection.read() {
status_line.selected_revset = Some(selection.clone());
}
}
impl Widget for &StatusLine {
fn render(self, status_line_area: Rect, buf: &mut Buffer)
where
Self: Sized,
{
let selected_revset = match &self.selected_revset {
Some(RevisionSelection::Single(rev)) => {
let change_id = rev.change_id.into_parts();
Line::from(vec![
Span::styled(change_id.head, Style::new().magenta()),
Span::styled(change_id.tail, Style::new().black()),
])
}
Some(RevisionSelection::Range(start, end)) => {
let start_change_id = start.change_id.into_parts();
let end_change_id = end.change_id.into_parts();
Line::from(vec![
Span::styled(start_change_id.head, Style::new().magenta()),
Span::styled(start_change_id.tail, Style::new().black()),
Span::styled("..", Style::new().black()),
Span::styled(end_change_id.head, Style::new().magenta()),
Span::styled(end_change_id.tail, Style::new().black()),
])
}
None => "-".into(),
};
let revset = self.logged_revset.clone().unwrap_or("-".into());
let logged_revset = Span::styled(&revset, Style::new().black());
let [_, selected_revset_area, logged_revset_area] = Layout::default()
.direction(Direction::Horizontal)
.horizontal_margin(1)
.spacing(2)
.constraints([
Constraint::Fill(1),
Constraint::Length(selected_revset.width() as u16),
Constraint::Length(revset.len().try_into().unwrap()),
])
.areas(status_line_area);
Block::default().on_white().render(status_line_area, buf);
selected_revset.render(selected_revset_area, buf);
logged_revset.render(logged_revset_area, buf);
}
}
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use ratatui::backend::TestBackend;
use crate::backend::revisions::{ChangeId, Revision};
use super::*;
#[test]
fn snapshot_selected_revset_single() {
let line = StatusLine {
selected_revset: Some(RevisionSelection::Single(Revision {
change_id: ChangeId("foo".into(), 3),
..default()
})),
..default()
};
let mut terminal = Terminal::new(TestBackend::new(80, 20)).unwrap();
terminal
.draw(|frame| frame.render_widget(&line, frame.area()))
.unwrap();
assert_snapshot!(terminal.backend());
}
#[test]
fn snapshot_selected_revset_range() {
let line = StatusLine {
selected_revset: Some(RevisionSelection::Range(
Revision {
change_id: ChangeId("foo".into(), 3),
..default()
},
Revision {
change_id: ChangeId("bar".into(), 3),
..default()
},
)),
..default()
};
let mut terminal = Terminal::new(TestBackend::new(80, 20)).unwrap();
terminal
.draw(|frame| frame.render_widget(&line, frame.area()))
.unwrap();
assert_snapshot!(terminal.backend());
}
#[test]
fn snapshot_logged_revset() {
let line = StatusLine {
logged_revset: Some("lel".into()),
..default()
};
let mut terminal = Terminal::new(TestBackend::new(80, 20)).unwrap();
terminal
.draw(|frame| frame.render_widget(&line, frame.area()))
.unwrap();
assert_snapshot!(terminal.backend());
}
}