git_iblame/
cli.rs

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                        // Invalidate because the "working" message cleared the screen.
60                        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}