git-iblame 0.8.10

Interactive enhanced `git blame` command line tool.
Documentation
use std::{
    io::{Write, stdout},
    time::{SystemTime, UNIX_EPOCH},
};

use crossterm::{cursor, queue, style, terminal};

#[derive(Debug, Default)]
pub enum CommandPrompt {
    #[default]
    None,
    Loading,
    Message {
        message: String,
    },
    Err {
        error: anyhow::Error,
    },
}

impl CommandPrompt {
    pub fn show(&self, row: u16, buffer: &str) -> anyhow::Result<()> {
        let mut out = stdout();
        queue!(
            out,
            cursor::MoveTo(0, row),
            terminal::Clear(terminal::ClearType::CurrentLine),
        )?;
        let mut suppress_help = false;
        match self {
            CommandPrompt::None => {}
            CommandPrompt::Loading => {
                let icon = Self::loading_indicator()?;
                queue!(out, style::Print(icon.to_string()),)?;
            }
            CommandPrompt::Message { message } => {
                queue!(out, style::Print(message.to_string()),)?;
                suppress_help = true;
            }
            CommandPrompt::Err { error } => {
                let error_message = error.to_string();
                queue!(
                    out,
                    style::SetColors(style::Colors::new(style::Color::White, style::Color::Red)),
                    style::Print(error_message),
                    style::ResetColor
                )?;
                suppress_help = true;
            }
        }
        if buffer.starts_with('/') {
            queue!(out, style::Print(buffer))?;
        } else {
            queue!(out, style::Print(format!(":{buffer}")))?;
            if !suppress_help && buffer.is_empty() {
                queue!(
                    out,
                    cursor::SavePosition,
                    style::SetForegroundColor(style::Color::DarkGrey),
                    style::Print("h(elp), q(uit), Right=parent, s(how), d(iff)"),
                    style::ResetColor,
                    cursor::RestorePosition,
                )?;
            }
        }
        out.flush()?;
        Ok(())
    }

    const ICON_CYCLE: &str = r"-\|/";

    fn loading_indicator() -> anyhow::Result<char> {
        let now = SystemTime::now();
        let duration = now.duration_since(UNIX_EPOCH)?;
        let index = (duration.as_secs() % (Self::ICON_CYCLE.len() as u64)) as usize;
        let icon = Self::ICON_CYCLE.chars().nth(index).unwrap();
        Ok(icon)
    }
}