diff --git a/crates/cli/src/tui/components/history.rs b/crates/cli/src/tui/components/history.rs index ba63ce05..6f42fa7a 100644 --- a/crates/cli/src/tui/components/history.rs +++ b/crates/cli/src/tui/components/history.rs @@ -1,9 +1,13 @@ // use git_next_core::git::graph::Log; +use lazy_static::lazy_static; use ratatui::{ - text::{Line, Text}, + style::{Color, Style, Styled as _}, + text::{Line, Span, Text}, widgets::{Paragraph, Widget}, }; +use regex::Regex; +use tracing::info; use super::HeightContraintLength; @@ -39,8 +43,88 @@ impl LogLine { Self { raw: raw.into() } } } +lazy_static::lazy_static! { + static ref RE: Regex = + #[allow(clippy::unwrap_used)] + Regex::new( + r"^(?
.*)\s(?[0-9a-f]{7})\s\((?.*?)\)\s(?.*)",
+        ).unwrap();
+
+    static ref BRANCHES: Regex =
+    #[allow(clippy::unwrap_used)]
+        Regex::new(
+            r"origin\/(?[^,]+)",
+        ).unwrap();
+}
 impl From for Line<'_> {
     fn from(value: LogLine) -> Self {
-        Line::from(value.raw)
+        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::>();
+            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> {
+        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(())
+            },
+        )
     }
 }