mirror of
https://github.com/gitui-org/gitui
synced 2026-05-22 16:38:28 +00:00
Add first snapshot tests using insta (#2813)
This commit is contained in:
parent
b1db21e10a
commit
7c538e3873
9 changed files with 412 additions and 118 deletions
38
Cargo.lock
generated
38
Cargo.lock
generated
|
|
@ -498,6 +498,18 @@ dependencies = [
|
|||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
|
|
@ -910,6 +922,12 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
|
|
@ -1314,7 +1332,9 @@ dependencies = [
|
|||
"filetreelist",
|
||||
"fuzzy-matcher",
|
||||
"gh-emoji",
|
||||
"git2-testing",
|
||||
"indexmap",
|
||||
"insta",
|
||||
"itertools",
|
||||
"log",
|
||||
"notify",
|
||||
|
|
@ -2408,6 +2428,18 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.44.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5c943d4415edd8153251b6f197de5eb1640e56d84e8d9159bea190421c73698"
|
||||
dependencies = [
|
||||
"console",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"similar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.6"
|
||||
|
|
@ -3881,6 +3913,12 @@ dependencies = [
|
|||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
version = "0.12.2"
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ which = "8.0"
|
|||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.11"
|
||||
git2-testing = { path = "./git2-testing" }
|
||||
insta = { version = "1.41.0", features = ["filters"] }
|
||||
pretty_assertions = "1.4"
|
||||
tempfile = "3"
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,18 @@ pub fn repo_init_empty() -> (TempDir, Repository) {
|
|||
(td, repo)
|
||||
}
|
||||
|
||||
/// initialize test repo in temp path with an empty first commit
|
||||
pub fn repo_init() -> (TempDir, Repository) {
|
||||
/// initialize test repo in temp path with given suffix and an empty first commit
|
||||
pub fn repo_init_suffix<T: AsRef<std::ffi::OsStr>>(
|
||||
suffix: Option<T>,
|
||||
) -> (TempDir, Repository) {
|
||||
init_log();
|
||||
|
||||
sandbox_config_files();
|
||||
|
||||
let td = TempDir::new().unwrap();
|
||||
let td = match suffix {
|
||||
Some(suffix) => TempDir::with_suffix(suffix).unwrap(),
|
||||
None => TempDir::new().unwrap(),
|
||||
};
|
||||
let repo = Repository::init(td.path()).unwrap();
|
||||
{
|
||||
let mut config = repo.config().unwrap();
|
||||
|
|
@ -45,6 +50,11 @@ pub fn repo_init() -> (TempDir, Repository) {
|
|||
(td, repo)
|
||||
}
|
||||
|
||||
/// initialize test repo in temp path with an empty first commit
|
||||
pub fn repo_init() -> (TempDir, Repository) {
|
||||
repo_init_suffix::<&std::ffi::OsStr>(None)
|
||||
}
|
||||
|
||||
// init log
|
||||
fn init_log() {
|
||||
let _ = env_logger::builder()
|
||||
|
|
|
|||
290
src/gitui.rs
Normal file
290
src/gitui.rs
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use asyncgit::{sync::utils::repo_work_dir, AsyncGitNotification};
|
||||
use crossbeam_channel::{never, tick, unbounded, Receiver};
|
||||
use scopetime::scope_time;
|
||||
|
||||
#[cfg(test)]
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
|
||||
use crate::{
|
||||
app::{App, QuitState},
|
||||
args::CliArgs,
|
||||
draw,
|
||||
input::{Input, InputEvent, InputState},
|
||||
keys::KeyConfig,
|
||||
select_event,
|
||||
spinner::Spinner,
|
||||
ui::style::Theme,
|
||||
watcher::RepoWatcher,
|
||||
AsyncAppNotification, AsyncNotification, QueueEvent, Updater,
|
||||
SPINNER_INTERVAL, TICK_INTERVAL,
|
||||
};
|
||||
|
||||
pub struct Gitui {
|
||||
app: crate::app::App,
|
||||
rx_input: Receiver<InputEvent>,
|
||||
rx_git: Receiver<AsyncGitNotification>,
|
||||
rx_app: Receiver<AsyncAppNotification>,
|
||||
rx_ticker: Receiver<Instant>,
|
||||
rx_watcher: Receiver<()>,
|
||||
}
|
||||
|
||||
impl Gitui {
|
||||
pub(crate) fn new(
|
||||
cliargs: CliArgs,
|
||||
theme: Theme,
|
||||
key_config: &KeyConfig,
|
||||
updater: Updater,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let (tx_git, rx_git) = unbounded();
|
||||
let (tx_app, rx_app) = unbounded();
|
||||
|
||||
let input = Input::new();
|
||||
|
||||
let (rx_ticker, rx_watcher) = match updater {
|
||||
Updater::NotifyWatcher => {
|
||||
let repo_watcher = RepoWatcher::new(
|
||||
repo_work_dir(&cliargs.repo_path)?.as_str(),
|
||||
);
|
||||
|
||||
(never(), repo_watcher.receiver())
|
||||
}
|
||||
Updater::Ticker => (tick(TICK_INTERVAL), never()),
|
||||
};
|
||||
|
||||
let app = App::new(
|
||||
cliargs,
|
||||
tx_git,
|
||||
tx_app,
|
||||
input.clone(),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
app,
|
||||
rx_input: input.receiver(),
|
||||
rx_git,
|
||||
rx_app,
|
||||
rx_ticker,
|
||||
rx_watcher,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn run_main_loop<B: ratatui::backend::Backend>(
|
||||
&mut self,
|
||||
terminal: &mut ratatui::Terminal<B>,
|
||||
) -> Result<QuitState, anyhow::Error>
|
||||
where
|
||||
<B as ratatui::backend::Backend>::Error:
|
||||
'static + Send + Sync,
|
||||
{
|
||||
let spinner_ticker = tick(SPINNER_INTERVAL);
|
||||
let mut spinner = Spinner::default();
|
||||
let mut first_update = true;
|
||||
|
||||
self.app.update()?;
|
||||
|
||||
loop {
|
||||
let event = if first_update {
|
||||
first_update = false;
|
||||
QueueEvent::Notify
|
||||
} else {
|
||||
select_event(
|
||||
&self.rx_input,
|
||||
&self.rx_git,
|
||||
&self.rx_app,
|
||||
&self.rx_ticker,
|
||||
&self.rx_watcher,
|
||||
&spinner_ticker,
|
||||
)?
|
||||
};
|
||||
|
||||
{
|
||||
if matches!(event, QueueEvent::SpinnerUpdate) {
|
||||
spinner.update();
|
||||
spinner.draw(terminal)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
scope_time!("loop");
|
||||
|
||||
match event {
|
||||
QueueEvent::InputEvent(ev) => {
|
||||
if matches!(
|
||||
ev,
|
||||
InputEvent::State(InputState::Polling)
|
||||
) {
|
||||
//Note: external ed closed, we need to re-hide cursor
|
||||
terminal.hide_cursor()?;
|
||||
}
|
||||
self.app.event(ev)?;
|
||||
}
|
||||
QueueEvent::Tick | QueueEvent::Notify => {
|
||||
self.app.update()?;
|
||||
}
|
||||
QueueEvent::AsyncEvent(ev) => {
|
||||
if !matches!(
|
||||
ev,
|
||||
AsyncNotification::Git(
|
||||
AsyncGitNotification::FinishUnchanged
|
||||
)
|
||||
) {
|
||||
self.app.update_async(ev)?;
|
||||
}
|
||||
}
|
||||
QueueEvent::SpinnerUpdate => unreachable!(),
|
||||
}
|
||||
|
||||
self.draw(terminal)?;
|
||||
|
||||
spinner.set_state(self.app.any_work_pending());
|
||||
spinner.draw(terminal)?;
|
||||
|
||||
if self.app.is_quit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.app.quit_state())
|
||||
}
|
||||
|
||||
fn draw<B: ratatui::backend::Backend>(
|
||||
&self,
|
||||
terminal: &mut ratatui::Terminal<B>,
|
||||
) -> Result<(), B::Error> {
|
||||
draw(terminal, &self.app)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn update_async(&mut self, event: crate::AsyncNotification) {
|
||||
self.app.update_async(event).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn input_event(
|
||||
&mut self,
|
||||
code: KeyCode,
|
||||
modifiers: KeyModifiers,
|
||||
) {
|
||||
let event = crossterm::event::KeyEvent::new(code, modifiers);
|
||||
self.app
|
||||
.event(crate::input::InputEvent::Input(
|
||||
crossterm::event::Event::Key(event),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn wait_for_async_git_notification(
|
||||
&self,
|
||||
expected: AsyncGitNotification,
|
||||
) {
|
||||
loop {
|
||||
let actual = self
|
||||
.rx_git
|
||||
.recv_timeout(std::time::Duration::from_millis(100))
|
||||
.unwrap();
|
||||
|
||||
if actual == expected {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn update(&mut self) {
|
||||
self.app.update().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use asyncgit::{sync::RepoPath, AsyncGitNotification};
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use git2_testing::repo_init_suffix;
|
||||
use insta::assert_snapshot;
|
||||
use ratatui::{backend::TestBackend, Terminal};
|
||||
|
||||
use crate::{
|
||||
args::CliArgs, gitui::Gitui, keys::KeyConfig,
|
||||
ui::style::Theme, AsyncNotification, Updater,
|
||||
};
|
||||
|
||||
// Macro adapted from: https://insta.rs/docs/cmd/
|
||||
macro_rules! apply_common_filters {
|
||||
{} => {
|
||||
let mut settings = insta::Settings::clone_current();
|
||||
// Windows and MacOS
|
||||
// We don't match on the full path, but on the suffix we pass to `repo_init_suffix` below.
|
||||
settings.add_filter(r" *\[…\]\S+-insta/?", "[TEMP_FILE]");
|
||||
// Linux Temp Folder
|
||||
settings.add_filter(r" */tmp/\.tmp\S+-insta/", "[TEMP_FILE]");
|
||||
// Commit ids that follow a vertical bar
|
||||
settings.add_filter(r"│[a-z0-9]{7} ", "│[AAAAA] ");
|
||||
let _bound = settings.bind_to_scope();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gitui_starts() {
|
||||
apply_common_filters!();
|
||||
|
||||
let (temp_dir, _repo) = repo_init_suffix(Some("-insta"));
|
||||
let path: RepoPath = temp_dir.path().to_str().unwrap().into();
|
||||
let cliargs = CliArgs {
|
||||
theme: PathBuf::from("theme.ron"),
|
||||
select_file: None,
|
||||
repo_path: path,
|
||||
notify_watcher: false,
|
||||
key_bindings_path: None,
|
||||
key_symbols_path: None,
|
||||
};
|
||||
|
||||
let theme = Theme::init(&PathBuf::new());
|
||||
let key_config = KeyConfig::default();
|
||||
|
||||
let mut gitui =
|
||||
Gitui::new(cliargs, theme, &key_config, Updater::Ticker)
|
||||
.unwrap();
|
||||
|
||||
let mut terminal =
|
||||
Terminal::new(TestBackend::new(90, 12)).unwrap();
|
||||
|
||||
gitui.draw(&mut terminal).unwrap();
|
||||
|
||||
assert_snapshot!("app_loading", terminal.backend());
|
||||
|
||||
let event =
|
||||
AsyncNotification::Git(AsyncGitNotification::Status);
|
||||
gitui.update_async(event);
|
||||
|
||||
gitui.draw(&mut terminal).unwrap();
|
||||
|
||||
assert_snapshot!("app_loading_finished", terminal.backend());
|
||||
|
||||
gitui.input_event(KeyCode::Char('2'), KeyModifiers::empty());
|
||||
gitui.input_event(
|
||||
key_config.keys.tab_log.code,
|
||||
key_config.keys.tab_log.modifiers,
|
||||
);
|
||||
|
||||
gitui.wait_for_async_git_notification(
|
||||
AsyncGitNotification::Log,
|
||||
);
|
||||
|
||||
gitui.update();
|
||||
|
||||
gitui.draw(&mut terminal).unwrap();
|
||||
|
||||
assert_snapshot!(
|
||||
"app_log_tab_showing_one_commit",
|
||||
terminal.backend()
|
||||
);
|
||||
}
|
||||
}
|
||||
120
src/main.rs
120
src/main.rs
|
|
@ -65,6 +65,7 @@ mod bug_report;
|
|||
mod clipboard;
|
||||
mod cmdbar;
|
||||
mod components;
|
||||
mod gitui;
|
||||
mod input;
|
||||
mod keys;
|
||||
mod notify_mutex;
|
||||
|
|
@ -85,12 +86,9 @@ use crate::{
|
|||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use app::QuitState;
|
||||
use asyncgit::{
|
||||
sync::{utils::repo_work_dir, RepoPath},
|
||||
AsyncGitNotification,
|
||||
};
|
||||
use asyncgit::{sync::RepoPath, AsyncGitNotification};
|
||||
use backtrace::Backtrace;
|
||||
use crossbeam_channel::{never, tick, unbounded, Receiver, Select};
|
||||
use crossbeam_channel::{Receiver, Select};
|
||||
use crossterm::{
|
||||
terminal::{
|
||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen,
|
||||
|
|
@ -98,12 +96,11 @@ use crossterm::{
|
|||
},
|
||||
ExecutableCommand,
|
||||
};
|
||||
use input::{Input, InputEvent, InputState};
|
||||
use gitui::Gitui;
|
||||
use input::InputEvent;
|
||||
use keys::KeyConfig;
|
||||
use ratatui::backend::CrosstermBackend;
|
||||
use scopeguard::defer;
|
||||
use scopetime::scope_time;
|
||||
use spinner::Spinner;
|
||||
use std::{
|
||||
io::{self, Stdout},
|
||||
panic,
|
||||
|
|
@ -111,7 +108,6 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
use ui::style::Theme;
|
||||
use watcher::RepoWatcher;
|
||||
|
||||
type Terminal = ratatui::Terminal<CrosstermBackend<io::Stdout>>;
|
||||
|
||||
|
|
@ -187,7 +183,6 @@ fn main() -> Result<()> {
|
|||
|
||||
let mut terminal =
|
||||
start_terminal(io::stdout(), &cliargs.repo_path)?;
|
||||
let input = Input::new();
|
||||
|
||||
let updater = if cliargs.notify_watcher {
|
||||
Updater::NotifyWatcher
|
||||
|
|
@ -202,8 +197,7 @@ fn main() -> Result<()> {
|
|||
app_start,
|
||||
args.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
&input,
|
||||
&key_config,
|
||||
updater,
|
||||
&mut terminal,
|
||||
)?;
|
||||
|
|
@ -230,106 +224,15 @@ fn run_app(
|
|||
app_start: Instant,
|
||||
cliargs: CliArgs,
|
||||
theme: Theme,
|
||||
key_config: KeyConfig,
|
||||
input: &Input,
|
||||
key_config: &KeyConfig,
|
||||
updater: Updater,
|
||||
terminal: &mut Terminal,
|
||||
) -> Result<QuitState, anyhow::Error> {
|
||||
let (tx_git, rx_git) = unbounded();
|
||||
let (tx_app, rx_app) = unbounded();
|
||||
|
||||
let rx_input = input.receiver();
|
||||
|
||||
let (rx_ticker, rx_watcher) = match updater {
|
||||
Updater::NotifyWatcher => {
|
||||
let repo_watcher = RepoWatcher::new(
|
||||
repo_work_dir(&cliargs.repo_path)?.as_str(),
|
||||
);
|
||||
|
||||
(never(), repo_watcher.receiver())
|
||||
}
|
||||
Updater::Ticker => (tick(TICK_INTERVAL), never()),
|
||||
};
|
||||
|
||||
let spinner_ticker = tick(SPINNER_INTERVAL);
|
||||
|
||||
let mut app = App::new(
|
||||
cliargs,
|
||||
tx_git,
|
||||
tx_app,
|
||||
input.clone(),
|
||||
theme,
|
||||
key_config,
|
||||
)?;
|
||||
|
||||
let mut spinner = Spinner::default();
|
||||
let mut first_update = true;
|
||||
let mut gitui = Gitui::new(cliargs, theme, key_config, updater)?;
|
||||
|
||||
log::trace!("app start: {} ms", app_start.elapsed().as_millis());
|
||||
|
||||
loop {
|
||||
let event = if first_update {
|
||||
first_update = false;
|
||||
QueueEvent::Notify
|
||||
} else {
|
||||
select_event(
|
||||
&rx_input,
|
||||
&rx_git,
|
||||
&rx_app,
|
||||
&rx_ticker,
|
||||
&rx_watcher,
|
||||
&spinner_ticker,
|
||||
)?
|
||||
};
|
||||
|
||||
{
|
||||
if matches!(event, QueueEvent::SpinnerUpdate) {
|
||||
spinner.update();
|
||||
spinner.draw(terminal)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
scope_time!("loop");
|
||||
|
||||
match event {
|
||||
QueueEvent::InputEvent(ev) => {
|
||||
if matches!(
|
||||
ev,
|
||||
InputEvent::State(InputState::Polling)
|
||||
) {
|
||||
//Note: external ed closed, we need to re-hide cursor
|
||||
terminal.hide_cursor()?;
|
||||
}
|
||||
app.event(ev)?;
|
||||
}
|
||||
QueueEvent::Tick | QueueEvent::Notify => {
|
||||
app.update()?;
|
||||
}
|
||||
QueueEvent::AsyncEvent(ev) => {
|
||||
if !matches!(
|
||||
ev,
|
||||
AsyncNotification::Git(
|
||||
AsyncGitNotification::FinishUnchanged
|
||||
)
|
||||
) {
|
||||
app.update_async(ev)?;
|
||||
}
|
||||
}
|
||||
QueueEvent::SpinnerUpdate => unreachable!(),
|
||||
}
|
||||
|
||||
draw(terminal, &app)?;
|
||||
|
||||
spinner.set_state(app.any_work_pending());
|
||||
spinner.draw(terminal)?;
|
||||
|
||||
if app.is_quit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(app.quit_state())
|
||||
gitui.run_main_loop(terminal)
|
||||
}
|
||||
|
||||
fn setup_terminal() -> Result<()> {
|
||||
|
|
@ -353,7 +256,10 @@ fn shutdown_terminal() {
|
|||
}
|
||||
}
|
||||
|
||||
fn draw(terminal: &mut Terminal, app: &App) -> io::Result<()> {
|
||||
fn draw<B: ratatui::backend::Backend>(
|
||||
terminal: &mut ratatui::Terminal<B>,
|
||||
app: &App,
|
||||
) -> Result<(), B::Error> {
|
||||
if app.requires_redraw() {
|
||||
terminal.clear()?;
|
||||
}
|
||||
|
|
|
|||
17
src/snapshots/gitui__gitui__tests__app_loading.snap
Normal file
17
src/snapshots/gitui__gitui__tests__app_loading.snap
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: src/gitui.rs
|
||||
expression: terminal.backend()
|
||||
snapshot_kind: text
|
||||
---
|
||||
" Status [1] | Log [2] | Files [3] | Stashing [4] | Stashes [5][TEMP_FILE] "
|
||||
" ──────────────────────────────────────────────────────────────────────────────────────── "
|
||||
"┌Unstaged Changes───────────────────────────┐┌Diff: ─────────────────────────────────────┐"
|
||||
"│Loading ... ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"└───────────────────────────────────{master}┘│ │"
|
||||
"┌Staged Changes─────────────────────────────┐│ │"
|
||||
"│Loading ... ││ │"
|
||||
"│ ││ │"
|
||||
"└───────────────────────────────────────────┘└───────────────────────────────────────────┘"
|
||||
" "
|
||||
17
src/snapshots/gitui__gitui__tests__app_loading_finished.snap
Normal file
17
src/snapshots/gitui__gitui__tests__app_loading_finished.snap
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: src/gitui.rs
|
||||
expression: terminal.backend()
|
||||
snapshot_kind: text
|
||||
---
|
||||
" Status [1] | Log [2] | Files [3] | Stashing [4] | Stashes [5][TEMP_FILE] "
|
||||
" ──────────────────────────────────────────────────────────────────────────────────────── "
|
||||
"┌Unstaged Changes───────────────────────────┐┌Diff: ─────────────────────────────────────┐"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"└───────────────────────────────────{master}┘│ │"
|
||||
"┌Staged Changes─────────────────────────────┐│ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"└───────────────────────────────────────────┘└───────────────────────────────────────────┘"
|
||||
"Branches [b] Push [p] Fetch [⇧F] Pull [f] Undo Commit [⇧U] Submodules [⇧S] more [.]"
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: src/gitui.rs
|
||||
expression: terminal.backend()
|
||||
snapshot_kind: text
|
||||
---
|
||||
" Status [1] | Log [2] | Files [3] | Stashing [4] | Stashes [5][TEMP_FILE] "
|
||||
" ──────────────────────────────────────────────────────────────────────────────────────── "
|
||||
"┌Commit 1/1──────────────────────────────────────────────────────────────────────────────┐"
|
||||
"│[AAAAA] <1m ago name initial █"
|
||||
"│ ║"
|
||||
"│ ║"
|
||||
"│ ║"
|
||||
"│ ║"
|
||||
"│ ║"
|
||||
"│ ║"
|
||||
"└────────────────────────────────────────────────────────────────────────────────────────┘"
|
||||
"Scroll [↑↓] Mark [˽] Details [⏎] Branches [b] Compare [⇧C] Copy Hash [y] Tag [t] more [.]"
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
use ratatui::{
|
||||
backend::{Backend, CrosstermBackend},
|
||||
Terminal,
|
||||
};
|
||||
use std::{cell::Cell, char, io};
|
||||
use ratatui::{backend::Backend, Terminal};
|
||||
use std::{cell::Cell, char};
|
||||
|
||||
// static SPINNER_CHARS: &[char] = &['◢', '◣', '◤', '◥'];
|
||||
// static SPINNER_CHARS: &[char] = &['⢹', '⢺', '⢼', '⣸', '⣇', '⡧', '⡗', '⡏'];
|
||||
|
|
@ -39,10 +36,10 @@ impl Spinner {
|
|||
}
|
||||
|
||||
/// draws or removes spinner char depending on `pending` state
|
||||
pub fn draw(
|
||||
pub fn draw<B: ratatui::backend::Backend>(
|
||||
&self,
|
||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||
) -> io::Result<()> {
|
||||
terminal: &mut Terminal<B>,
|
||||
) -> Result<(), B::Error> {
|
||||
let idx = self.idx;
|
||||
|
||||
let char_to_draw =
|
||||
|
|
|
|||
Loading…
Reference in a new issue