feat: use termwiz as backend as that enables a ptty, which can be cleaned up nicely
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Kasper Juul Hermansen 2024-09-23 22:16:19 +02:00
parent f0f81f8a0b
commit 348e448ce9
Signed by: kjuulh
SSH Key Fingerprint: SHA256:RjXh0p7U6opxnfd3ga/Y9TCo18FYlHFdSpRIV72S/QM
10 changed files with 663 additions and 107 deletions

705
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,6 @@ resolver = "2"
version = "0.2.0" version = "0.2.0"
[workspace.dependencies] [workspace.dependencies]
gitnow = { path = "crates/gitnow" }
anyhow = { version = "1" } anyhow = { version = "1" }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }

View File

@ -25,10 +25,10 @@ prost = "0.13.2"
prost-types = "0.13.2" prost-types = "0.13.2"
bytes = "1.7.1" bytes = "1.7.1"
nucleo-matcher = "0.3.1" nucleo-matcher = "0.3.1"
ratatui = "0.28.1" ratatui = { version = "0.28.1", features = ["termwiz"] }
crossterm = { version = "0.28.0", features = ["event-stream"] } crossterm = { version = "0.28.0", features = ["event-stream"] }
futures = "0.3.30" futures = "0.3.30"
atty = "0.2.14" termwiz = "0.22.0"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"

View File

@ -1,5 +1,5 @@
function git-now { function git-now {
choice=$(gitnow "$@") choice=$(gitnow "$@" --no-shell)
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
return $? return $?
fi fi

View File

@ -1,4 +1,4 @@
use std::collections::BTreeMap; use std::{collections::BTreeMap, io::IsTerminal};
use crate::{ use crate::{
app::App, app::App,
@ -88,7 +88,7 @@ impl RootCommand {
if clone { if clone {
let git_clone = self.app.git_clone(); let git_clone = self.app.git_clone();
if atty::is(atty::Stream::Stdout) && shell { if std::io::stdout().is_terminal() && shell {
let mut wrap_cmd = let mut wrap_cmd =
InlineCommand::new(format!("cloning: {}", repo.to_rel_path().display())); InlineCommand::new(format!("cloning: {}", repo.to_rel_path().display()));
let repo = repo.clone(); let repo = repo.clone();

View File

@ -1,4 +1,3 @@
use anyhow::Error;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
pub mod inline_command; pub mod inline_command;

View File

@ -1,23 +1,15 @@
use std::{ use std::time::Duration;
io::{stderr, Write},
time::Duration,
};
use anyhow::Context; use crossterm::event::{EventStream, KeyCode};
use crossterm::{
event::{EventStream, KeyCode, KeyEventKind},
terminal::{enable_raw_mode, EnterAlternateScreen},
ExecutableCommand,
};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use ratatui::{ use ratatui::{
crossterm, crossterm,
prelude::*, prelude::*,
widgets::{Block, Padding, Paragraph}, widgets::{Block, Padding},
TerminalOptions, Viewport, TerminalOptions, Viewport,
}; };
use crate::components::{BatchCommand, Command}; use crate::components::BatchCommand;
use super::{ use super::{
create_dispatch, create_dispatch,

View File

@ -1,11 +1,11 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use ratatui::{ use ratatui::{
text::{Line, Span, Text}, text::{Line, Span},
widgets::{Block, Paragraph, StatefulWidget, Widget}, widgets::{Block, Paragraph, StatefulWidget, Widget},
}; };
use super::{BatchCommand, Command, IntoCommand, Msg}; use super::{BatchCommand, IntoCommand, Msg};
pub struct Spinner<'a> { pub struct Spinner<'a> {
span: Span<'a>, span: Span<'a>,
@ -68,8 +68,8 @@ impl Default for SpinnerState {
const MINIDOT_FRAMES: [&str; 10] = ["", "", "", "", "", "", "", "", "", ""]; const MINIDOT_FRAMES: [&str; 10] = ["", "", "", "", "", "", "", "", "", ""];
impl SpinnerState { impl SpinnerState {
pub fn update(&mut self, msg: &Msg) -> impl IntoCommand { pub fn update(&mut self, _msg: &Msg) -> impl IntoCommand {
let mut batch = BatchCommand::default(); let batch = BatchCommand::default();
let now = Instant::now(); let now = Instant::now();
if now.duration_since(self.last_event) >= self.interval { if now.duration_since(self.last_event) >= self.interval {

View File

@ -1,4 +1,4 @@
use crate::{app::App, components::inline_command::InlineCommand, git_provider::Repository}; use crate::{app::App, git_provider::Repository};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GitClone { pub struct GitClone {

View File

@ -1,11 +1,5 @@
use std::io::stderr;
use app::App; use app::App;
use crossterm::{ use ratatui::{prelude::*, Terminal};
terminal::{enable_raw_mode, EnterAlternateScreen},
ExecutableCommand,
};
use ratatui::{prelude::CrosstermBackend, Terminal};
use crate::git_provider::Repository; use crate::git_provider::Repository;
@ -22,16 +16,11 @@ impl Interactive {
&mut self, &mut self,
repositories: &[Repository], repositories: &[Repository],
) -> anyhow::Result<Option<Repository>> { ) -> anyhow::Result<Option<Repository>> {
let backend = CrosstermBackend::new(std::io::stderr()); let backend = TermwizBackend::new().map_err(|e| anyhow::anyhow!(e.to_string()))?;
let terminal = Terminal::new(backend)?; let terminal = Terminal::new(backend)?;
enable_raw_mode()?;
stderr().execute(EnterAlternateScreen)?;
let app_result = App::new(self.app, repositories).run(terminal); let app_result = App::new(self.app, repositories).run(terminal);
ratatui::restore();
app_result app_result
} }
} }
@ -50,7 +39,7 @@ mod app {
use ratatui::{ use ratatui::{
crossterm::event::{self, Event, KeyCode}, crossterm::event::{self, Event, KeyCode},
layout::{Constraint, Layout}, layout::{Constraint, Layout},
prelude::CrosstermBackend, prelude::TermwizBackend,
style::{Style, Stylize}, style::{Style, Stylize},
text::{Line, Span}, text::{Line, Span},
widgets::{ListItem, ListState, Paragraph, StatefulWidget}, widgets::{ListItem, ListState, Paragraph, StatefulWidget},
@ -86,8 +75,6 @@ mod app {
.fuzzy_matcher() .fuzzy_matcher()
.match_repositories(&self.current_search, self.repositories); .match_repositories(&self.current_search, self.repositories);
//res.reverse();
self.matched_repos = res; self.matched_repos = res;
if self.list.selected().is_none() { if self.list.selected().is_none() {
@ -95,9 +82,9 @@ mod app {
} }
} }
pub fn run<T: std::io::Write>( pub fn run(
mut self, mut self,
mut terminal: Terminal<CrosstermBackend<T>>, mut terminal: Terminal<TermwizBackend>,
) -> anyhow::Result<Option<Repository>> { ) -> anyhow::Result<Option<Repository>> {
self.update_matched_repos(); self.update_matched_repos();
@ -117,18 +104,16 @@ mod app {
} }
} }
KeyCode::Esc => { KeyCode::Esc => {
terminal.clear()?;
return Ok(None); return Ok(None);
} }
KeyCode::Enter => { KeyCode::Enter => {
if let Some(selected) = self.list.selected() { if let Some(selected) = self.list.selected() {
if let Some(repo) = self.matched_repos.get(selected).cloned() { if let Some(repo) = self.matched_repos.get(selected).cloned() {
terminal.clear()?; terminal.resize(ratatui::layout::Rect::ZERO)?;
return Ok(Some(repo)); return Ok(Some(repo));
} }
} }
terminal.clear()?;
return Ok(None); return Ok(None);
} }
KeyCode::Up => self.list.select_next(), KeyCode::Up => self.list.select_next(),