1use std::{io::stdout, path::Path};
2
3use crate::*;
4use crossterm::{clipboard::CopyToClipboard, cursor, execute, style, terminal};
5use git2::Oid;
6use log::*;
7
8pub struct Cli {
9 renderer: BlameRenderer,
10}
11
12impl Cli {
13 pub fn new(path: &Path) -> anyhow::Result<Self> {
14 Ok(Self {
15 renderer: BlameRenderer::new(path)?,
16 })
17 }
18
19 pub fn run(&mut self) -> anyhow::Result<()> {
20 terminal::enable_raw_mode()?;
21 let result = self.run_core();
22 if let Err(e) = terminal::disable_raw_mode() {
23 warn!("Failed to disable raw mode, ignored: {e}");
24 }
25 result
26 }
27
28 fn run_core(&mut self) -> anyhow::Result<()> {
29 let renderer = &mut self.renderer;
30 let size = terminal::size()?;
31 renderer.set_view_size((size.0, size.1 - 1));
32 renderer.read()?;
33 let mut history: Vec<Oid> = vec![];
34 let mut out = stdout();
35 let mut prompt: CommandPrompt = CommandPrompt::None;
36 loop {
37 renderer.render(&mut out)?;
38
39 let command = Command::read(renderer.rendered_rows(), &prompt)?;
40 prompt = CommandPrompt::None;
41 match command {
42 Command::PrevDiff => renderer.move_to_prev_diff(),
43 Command::NextDiff => renderer.move_to_next_diff(),
44 Command::PrevPage => renderer.move_to_prev_page(),
45 Command::NextPage => renderer.move_to_next_page(),
46 Command::FirstLine => renderer.move_to_first_line(),
47 Command::LastLine => renderer.move_to_last_line(),
48 Command::LineNumber(number) => renderer.set_current_line_number(number),
49 Command::Older => {
50 execute!(
51 out,
52 terminal::Clear(terminal::ClearType::All),
53 cursor::MoveTo(0, 0),
54 style::Print("Working...")
55 )?;
56 let old_commit_id = renderer.commit_id();
57 if let Err(error) = renderer.set_commit_id_to_older_than_current_line() {
58 prompt = CommandPrompt::Err { error };
59 renderer.invalidate_render();
61 continue;
62 }
63 history.push(old_commit_id);
64 }
65 Command::Newer => {
66 if let Some(commit_id) = history.pop() {
67 renderer.set_commit_id(commit_id)?;
68 }
69 }
70 Command::Copy => execute!(
71 out,
72 CopyToClipboard::to_clipboard_from(
73 renderer.current_line_commit_id().to_string()
74 )
75 )?,
76 Command::Resize(columns, rows) => renderer.set_view_size((columns, rows - 1)),
77 Command::Quit => break,
78 }
79 }
80 Ok(())
81 }
82}