git_iblame/
cli.rs

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