From 773b1bbb7b8b3704062859979ba8883c2fe9c426 Mon Sep 17 00:00:00 2001 From: Martin Lange Date: Tue, 29 Dec 2020 01:47:12 +0100 Subject: [PATCH] page output, switch to disable, removed no-color short switch --- Cargo.lock | 144 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 106 +++++++++++++++++++++++++++++-- src/print/unicode.rs | 22 +++---- 4 files changed, 258 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72852c3..4d06322 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,6 +77,31 @@ dependencies = [ "vec_map", ] +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags", + "crossterm_winapi", + "lazy_static", + "libc", + "mio", + "parking_lot", + "signal-hook", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi", +] + [[package]] name = "dirs-next" version = "1.0.2" @@ -131,6 +156,7 @@ version = "0.1.2" dependencies = [ "atty", "clap", + "crossterm", "git2", "itertools", "lazy_static", @@ -178,6 +204,15 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "itertools" version = "0.9.0" @@ -248,6 +283,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.11" @@ -269,6 +313,38 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "mio" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33bc887064ef1fd66020c9adfc45bb9f33d75a42096c81e7c56c65b75dd1a8b" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + [[package]] name = "openssl-probe" version = "0.1.2" @@ -288,6 +364,31 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -361,6 +462,12 @@ version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.118" @@ -378,6 +485,43 @@ dependencies = [ "syn", ] +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "mio", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi", +] + [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index c07f72d..6233a9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,4 @@ lazy_static = "1.4" yansi = "0.5" atty = "0.2" platform-dirs = "0.3" +crossterm = "0.19" diff --git a/src/main.rs b/src/main.rs index 2c9d029..4608dd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,9 @@ use clap::{crate_version, App, Arg, SubCommand}; +use crossterm::cursor::MoveToColumn; +use crossterm::event::{Event, KeyCode, KeyModifiers}; +use crossterm::style::Print; +use crossterm::terminal::{Clear, ClearType}; +use crossterm::{ErrorKind, ExecutableCommand}; use git2::{Error, Repository}; use git_graph::graph::{CommitInfo, GitGraph}; use git_graph::print::svg::print_svg; @@ -8,6 +13,7 @@ use git_graph::settings::{ Settings, }; use platform_dirs::AppDirs; +use std::io::stdout; use std::path::PathBuf; use std::str::FromStr; use std::time::Instant; @@ -88,12 +94,18 @@ fn from_args() -> Result<(), String> { ) .arg( Arg::with_name("no-color") - .long("no-color").alias("mono") - .short("M") + .long("no-color") .help("Print without colors. Missing color support should be detected automatically (e.g. when piping to a file).") .required(false) .takes_value(false), ) + .arg( + Arg::with_name("no-pager") + .long("no-pager") + .help("Use no pager (print everything at once without prompt).") + .required(false) + .takes_value(false), + ) .arg( Arg::with_name("style") .long("style") @@ -163,6 +175,7 @@ fn from_args() -> Result<(), String> { let svg = matches.is_present("svg"); let colored = !matches.is_present("no-color"); + let pager = !matches.is_present("no-pager"); let compact = !matches.is_present("sparse"); let debug = matches.is_present("debug"); let style = matches @@ -183,7 +196,7 @@ fn from_args() -> Result<(), String> { merge_patterns: MergePatterns::default(), }; - run(repository, &settings, svg, commit_limit) + run(repository, &settings, svg, commit_limit, pager) } fn get_model_name(repository: &Repository) -> Result, String> { @@ -335,6 +348,7 @@ fn run( settings: &Settings, svg: bool, max_commits: Option, + pager: bool, ) -> Result<(), String> { let now = Instant::now(); let graph = GitGraph::new(repository, settings, max_commits)?; @@ -367,7 +381,12 @@ fn run( if svg { println!("{}", print_svg(&graph, &settings)?); } else { - println!("{}", print_unicode(&graph, &settings)?); + let lines = print_unicode(&graph, &settings)?; + if pager && atty::is(atty::Stream::Stdout) { + print_paged(&lines).map_err(|err| err.to_string())?; + } else { + print_unpaged(&lines); + } }; let duration_print = now.elapsed().as_micros(); @@ -383,6 +402,85 @@ fn run( Ok(()) } +fn print_paged(lines: &[String]) -> Result<(), ErrorKind> { + let height = crossterm::terminal::size()?.1; + + let mut line_idx = 0; + let mut print_lines = height - 2; + let mut clear = false; + let mut abort = false; + + while line_idx < lines.len() { + if print_lines > 0 { + if clear { + stdout() + .execute(Clear(ClearType::CurrentLine))? + .execute(MoveToColumn(0))?; + } + + stdout().execute(Print(format!("{}\n", lines[line_idx])))?; + + if print_lines == 1 && line_idx < lines.len() - 1 { + stdout().execute(Print( + "Down: line, PgDown/Enter: page, End: all, Esc/Q/^C: quit", + ))?; + } + print_lines -= 1; + line_idx += 1; + } else { + let input = crossterm::event::read()?; + match input { + Event::Key(evt) => match evt.code { + KeyCode::Down => { + clear = true; + print_lines = 1; + } + KeyCode::Enter | KeyCode::PageDown => { + clear = true; + print_lines = height - 2; + } + KeyCode::End => { + clear = true; + print_lines = lines.len() as u16; + } + KeyCode::Char(c) => match c { + 'q' => { + abort = true; + break; + } + 'c' if evt.modifiers == KeyModifiers::CONTROL => { + abort = true; + break; + } + _ => {} + }, + KeyCode::Esc => { + abort = true; + break; + } + _ => {} + }, + Event::Mouse(_) => {} + Event::Resize(_, _) => {} + } + } + } + if abort { + stdout() + .execute(Clear(ClearType::CurrentLine))? + .execute(MoveToColumn(0))? + .execute(Print(" ...\n"))?; + } + + Ok(()) +} + +fn print_unpaged(lines: &[String]) { + for line in lines { + println!("{}", line); + } +} + fn print_commit_short(graph: &GitGraph, info: &CommitInfo) -> Result<(), Error> { let commit = &graph.commit(info.oid)?; let symbol = if commit.parents().len() > 1 { "o" } else { "*" }; diff --git a/src/print/unicode.rs b/src/print/unicode.rs index 9b612c2..fa2be55 100644 --- a/src/print/unicode.rs +++ b/src/print/unicode.rs @@ -1,6 +1,5 @@ use crate::graph::{GitGraph, HeadInfo}; use crate::settings::{Characters, Settings}; -use atty::Stream; use itertools::Itertools; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; @@ -27,7 +26,7 @@ const ARR_R: u8 = 15; const WHITE: u8 = 7; -pub fn print_unicode(graph: &GitGraph, settings: &Settings) -> Result { +pub fn print_unicode(graph: &GitGraph, settings: &Settings) -> Result, String> { let num_cols = 2 * graph .branches .iter() @@ -420,15 +419,17 @@ fn print_graph( characters: &Characters, grid: &Grid, color: bool, -) -> Result { - let color = - color && atty::is(Stream::Stdout) && (!cfg!(windows) || Paint::enable_windows_ascii()); +) -> Result, String> { + let color = color + && atty::is(atty::Stream::Stdout) + && (!cfg!(windows) || Paint::enable_windows_ascii()); + let mut lines = vec![]; let head_idx = graph.indices[&graph.head.oid]; - let mut out = String::new(); - for (line_idx, row) in grid.data.chunks(grid.width).enumerate() { + let mut out = String::new(); + let index = line_to_index.get(&line_idx); let head = if index.map(|idx| *idx == head_idx).unwrap_or(false) { Some(&graph.head) @@ -460,12 +461,10 @@ fn print_graph( } write_post(&mut out, &graph, index, head, color)?; - if line_idx < grid.height - 1 { - writeln!(out).map_err(|err| err.to_string())?; - } + lines.push(out); } - Ok(out) + Ok(lines) } fn write_pre( @@ -601,6 +600,7 @@ fn sorted(v1: usize, v2: usize) -> (usize, usize) { } } +#[allow(dead_code)] pub struct Grid { width: usize, height: usize,