git-next/crates/cli/src/tui/components/history.rs

131 lines
3.8 KiB
Rust
Raw Normal View History

//
use git_next_core::git::graph::Log;
2024-08-31 17:45:43 +01:00
use lazy_static::lazy_static;
use ratatui::{
2024-08-31 17:45:43 +01:00
style::{Color, Style, Styled as _},
text::{Line, Span, Text},
widgets::{Paragraph, Widget},
};
2024-08-31 17:45:43 +01:00
use regex::Regex;
use tracing::info;
use super::HeightContraintLength;
pub struct CommitLog<'a> {
pub log: &'a Log,
}
impl<'a> HeightContraintLength for CommitLog<'a> {
fn height_constraint_length(&self) -> u16 {
u16::try_from(self.log.len()).unwrap_or(u16::MAX)
}
}
impl<'a> Widget for CommitLog<'a> {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
Paragraph::new(Text::from(
self.log
.iter()
.map(LogLine::new)
.map(Line::from)
.collect::<Vec<_>>(),
))
.render(area, buf);
}
}
struct LogLine {
raw: String,
}
impl LogLine {
fn new(raw: impl Into<String>) -> Self {
Self { raw: raw.into() }
}
}
2024-08-31 17:45:43 +01:00
lazy_static::lazy_static! {
static ref RE: Regex =
#[allow(clippy::unwrap_used)]
Regex::new(
r"^(?<pre>.*)\s(?<hash>[0-9a-f]{7})\s\((?<branches>.*?)\)\s(?<message>.*)",
).unwrap();
static ref BRANCHES: Regex =
#[allow(clippy::unwrap_used)]
Regex::new(
r"origin\/(?<branch>[^,]+)",
).unwrap();
}
impl From<LogLine> for Line<'_> {
fn from(value: LogLine) -> Self {
2024-08-31 17:45:43 +01:00
if let Some(caps) = RE.captures(&value.raw) {
let pre = caps["pre"].to_owned();
let hash = caps["hash"].to_owned();
let message = caps["message"].to_owned();
info!(branches=?caps["branches"], "raw branches");
let mut branches = BRANCHES
.captures_iter(&caps["branches"])
.map(|captures| captures["branch"].to_owned())
.collect::<Vec<_>>();
if branches.is_empty() {
// line without branches
Line::from(vec![
pre.into(),
" ".into(),
hash.into(),
" ".into(),
message.into(),
])
} else {
// line withbranches
let mut spans = vec![pre.into(), " ".into(), hash.into(), " ".into()];
info!(?branches, "matched branch list");
branches.sort();
branches
.into_iter()
.map(|branch| Span::from(branch).style(Style::default().fg(Color::Blue)))
.for_each(|span| {
spans.push("[".into());
spans.push(span);
spans.push("]".into());
});
spans.push(" ".into());
spans.push(message.into());
Line::from(spans)
}
} else {
// non-commit line
Line::from(value.raw.clone())
}
}
}
#[cfg(test)]
mod tests {
use tracing::info;
use super::RE;
#[test_log::test]
fn parse_log_line() -> Result<(), Box<dyn std::error::Error>> {
let line = "* 97b6853 (origin/next, origin/main, origin/dev, origin/HEAD) refactor(tui): simplify repo identity widget";
RE.captures(line).map_or_else(
|| Err("Failed to capture".into()),
|caps| {
info!(?caps, "");
assert_eq!(&caps["pre"], "*");
assert_eq!(&caps["hash"], "97b6853");
assert_eq!(
&caps["branches"],
"origin/next, origin/main, origin/dev, origin/HEAD"
);
assert_eq!(
&caps["message"],
"refactor(tui): simplify repo identity widget"
);
Ok(())
},
)
}
}