feat: add errout for interactive for script support and atty for clean output
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
1
crates/gitnow/.gitignore
vendored
1
crates/gitnow/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/target
|
@@ -28,6 +28,7 @@ nucleo-matcher = "0.3.1"
|
||||
ratatui = "0.28.1"
|
||||
crossterm = { version = "0.28.0", features = ["event-stream"] }
|
||||
futures = "0.3.30"
|
||||
atty = "0.2.14"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
15
crates/gitnow/include/shell/zsh.sh
Normal file
15
crates/gitnow/include/shell/zsh.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
function git-now {
|
||||
choice=$(gitnow "$@")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return $?
|
||||
fi
|
||||
|
||||
cd "$(echo "$choice" | tail --lines 1)"
|
||||
}
|
||||
|
||||
function gn {
|
||||
git-now "$@"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return $?
|
||||
fi
|
||||
}
|
@@ -88,16 +88,21 @@ impl RootCommand {
|
||||
if clone {
|
||||
let git_clone = self.app.git_clone();
|
||||
|
||||
let mut wrap_cmd =
|
||||
InlineCommand::new(format!("cloning: {}", repo.to_rel_path().display()));
|
||||
let repo = repo.clone();
|
||||
wrap_cmd
|
||||
.execute(move || async move {
|
||||
git_clone.clone_repo(&repo, force_refresh).await?;
|
||||
if atty::is(atty::Stream::Stdout) && shell {
|
||||
let mut wrap_cmd =
|
||||
InlineCommand::new(format!("cloning: {}", repo.to_rel_path().display()));
|
||||
let repo = repo.clone();
|
||||
wrap_cmd
|
||||
.execute(move || async move {
|
||||
git_clone.clone_repo(&repo, force_refresh).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
eprintln!("cloning repository...");
|
||||
git_clone.clone_repo(&repo, force_refresh).await?;
|
||||
}
|
||||
} else {
|
||||
tracing::info!("skipping clone for repo: {}", &repo.to_rel_path().display());
|
||||
}
|
||||
|
@@ -0,0 +1,33 @@
|
||||
use zsh::ZshShell;
|
||||
|
||||
pub mod zsh;
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
pub struct Shell {
|
||||
#[command(subcommand)]
|
||||
shell: ShellSubcommands,
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
pub async fn execute(&mut self) -> anyhow::Result<()> {
|
||||
self.shell.execute().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand)]
|
||||
pub enum ShellSubcommands {
|
||||
#[command()]
|
||||
Zsh(ZshShell),
|
||||
}
|
||||
|
||||
impl ShellSubcommands {
|
||||
pub async fn execute(&mut self) -> anyhow::Result<()> {
|
||||
match self {
|
||||
ShellSubcommands::Zsh(zsh) => zsh.execute().await?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
12
crates/gitnow/src/commands/shell/zsh.rs
Normal file
12
crates/gitnow/src/commands/shell/zsh.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#[derive(clap::Parser)]
|
||||
pub struct ZshShell {}
|
||||
|
||||
const SCRIPT: &str = include_str!("../../../include/shell/zsh.sh");
|
||||
|
||||
impl ZshShell {
|
||||
pub async fn execute(&mut self) -> anyhow::Result<()> {
|
||||
println!("{}", SCRIPT);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -1,7 +1,14 @@
|
||||
use std::{io::Write, time::Duration};
|
||||
use std::{
|
||||
io::{stderr, Write},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use crossterm::event::{EventStream, KeyCode, KeyEventKind};
|
||||
use crossterm::{
|
||||
event::{EventStream, KeyCode, KeyEventKind},
|
||||
terminal::{enable_raw_mode, EnterAlternateScreen},
|
||||
ExecutableCommand,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ratatui::{
|
||||
crossterm,
|
||||
@@ -73,6 +80,8 @@ impl InlineCommand {
|
||||
|
||||
drop(guard);
|
||||
|
||||
println!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,11 @@
|
||||
use std::io::stderr;
|
||||
|
||||
use app::App;
|
||||
use crossterm::{
|
||||
terminal::{enable_raw_mode, EnterAlternateScreen},
|
||||
ExecutableCommand,
|
||||
};
|
||||
use ratatui::{prelude::CrosstermBackend, Terminal};
|
||||
|
||||
use crate::git_provider::Repository;
|
||||
|
||||
@@ -15,8 +22,14 @@ impl Interactive {
|
||||
&mut self,
|
||||
repositories: &[Repository],
|
||||
) -> anyhow::Result<Option<Repository>> {
|
||||
let terminal = ratatui::init();
|
||||
let backend = CrosstermBackend::new(std::io::stderr());
|
||||
let terminal = Terminal::new(backend)?;
|
||||
|
||||
enable_raw_mode()?;
|
||||
stderr().execute(EnterAlternateScreen)?;
|
||||
|
||||
let app_result = App::new(self.app, repositories).run(terminal);
|
||||
|
||||
ratatui::restore();
|
||||
|
||||
app_result
|
||||
@@ -37,10 +50,11 @@ mod app {
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event, KeyCode},
|
||||
layout::{Constraint, Layout},
|
||||
prelude::CrosstermBackend,
|
||||
style::{Style, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::{ListItem, ListState, Paragraph, StatefulWidget},
|
||||
DefaultTerminal, Frame,
|
||||
Frame, Terminal,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -81,7 +95,10 @@ mod app {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> anyhow::Result<Option<Repository>> {
|
||||
pub fn run<T: std::io::Write>(
|
||||
mut self,
|
||||
mut terminal: Terminal<CrosstermBackend<T>>,
|
||||
) -> anyhow::Result<Option<Repository>> {
|
||||
self.update_matched_repos();
|
||||
|
||||
loop {
|
||||
@@ -99,14 +116,19 @@ mod app {
|
||||
self.update_matched_repos();
|
||||
}
|
||||
}
|
||||
KeyCode::Esc => return Ok(None),
|
||||
KeyCode::Esc => {
|
||||
terminal.clear()?;
|
||||
return Ok(None);
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
if let Some(selected) = self.list.selected() {
|
||||
if let Some(repo) = self.matched_repos.get(selected).cloned() {
|
||||
terminal.clear()?;
|
||||
return Ok(Some(repo));
|
||||
}
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
return Ok(None);
|
||||
}
|
||||
KeyCode::Up => self.list.select_next(),
|
||||
|
@@ -4,8 +4,7 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::{Parser, Subcommand};
|
||||
use commands::root::RootCommand;
|
||||
use components::inline_command::InlineCommand;
|
||||
use commands::{root::RootCommand, shell::Shell};
|
||||
use config::Config;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
@@ -50,7 +49,7 @@ struct Command {
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Hello {},
|
||||
Init(Shell),
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_PATH: &str = ".config/gitnow/gitnow.toml";
|
||||
@@ -81,7 +80,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
tracing::debug!("Starting cli");
|
||||
|
||||
match cli.command {
|
||||
Some(_) => todo!(),
|
||||
Some(Commands::Init(mut shell)) => shell.execute().await?,
|
||||
None => {
|
||||
RootCommand::new(app)
|
||||
.execute(
|
||||
|
Reference in New Issue
Block a user