1use ratatui::{
2 layout::{Constraint, Layout, Rect},
3 style::{Color, Modifier, Style},
4 text::{Line, Span},
5 widgets::{Block, BorderType, Row, Table},
6 Frame,
7};
8
9use crate::{
10 app::App,
11 services::git::{DiffKind, DiffLine},
12};
13
14pub(super) fn render_body(model: &mut App, f: &mut Frame, area: Rect) {
15 let [left_side, right_side] =
17 Layout::horizontal(Constraint::from_percentages([50, 50])).areas(area);
18
19 let line_number_char_len = model.diff().unwrap().largest_line_number_char_len();
20
21 let old_diff = model.diff().unwrap().old_diff();
23 let old_diff_table = build_diff_table(old_diff, false, line_number_char_len);
24 let mut old_diff_state = model.diff_state().old_diff().borrow_mut();
25
26 let current_diff = model.diff().unwrap().current_diff();
28 let current_diff_table = build_diff_table(current_diff, true, line_number_char_len);
29 let mut current_diff_state = model.diff_state().current_diff().borrow_mut();
30
31 f.render_stateful_widget(old_diff_table, left_side, &mut old_diff_state);
32 f.render_stateful_widget(current_diff_table, right_side, &mut current_diff_state)
33}
34
35fn build_diff_table(diff: &[DiffLine], is_current_diff: bool, line_number_char_len: u16) -> Table {
37 let diff_title = if is_current_diff { "New" } else { "Original" };
38
39 let rows = diff.iter().map(parse_diff_line);
40
41 let widths = [
43 Constraint::Length(line_number_char_len),
45 Constraint::Percentage(2),
46 Constraint::Percentage(97),
47 ];
48
49 Table::new(rows, widths)
50 .block(
51 Block::bordered()
52 .title(Span::styled(
53 diff_title,
54 Style::default().fg(Color::LightCyan),
55 ))
56 .style(Style::default().fg(Color::White))
57 .border_type(BorderType::Plain),
58 )
59 .highlight_style(if is_current_diff {
60 Style::default()
61 .fg(Color::Magenta)
62 .add_modifier(Modifier::BOLD)
63 } else {
64 Style::default()
65 })
66 .highlight_symbol(">>")
67}
68
69fn parse_diff_line(line: &DiffLine) -> Row {
70 let line_number_style = Style::default().fg(Color::Gray);
72
73 let (prefix_style, content_style) = match line.kind() {
74 DiffKind::Addition => (
75 Style::default()
76 .fg(Color::Green)
77 .add_modifier(Modifier::BOLD),
78 Style::default()
79 .bg(Color::Rgb(131, 242, 140))
80 .fg(Color::Black),
81 ),
82 DiffKind::Removal => (
83 Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
84 Style::default().bg(Color::LightRed).fg(Color::Black),
85 ),
86 DiffKind::Neutral => (Style::default(), Style::default()),
87 DiffKind::Blank => (Style::default(), Style::default().bg(Color::DarkGray)),
88 };
89
90 let line_number = match line.line_number() {
91 Some(x) => x.to_string(),
92 None => " ".to_string(),
93 };
94
95 let prefix = line.kind().value();
96 let content = line.content();
97
98 Row::new([
99 Line::styled(line_number, line_number_style).right_aligned(),
100 Line::styled(prefix, prefix_style).centered(),
101 Line::styled(content, content_style),
102 ])
103}