1
0
mirror of https://github.com/sharkdp/bat.git synced 2025-09-21 20:52:26 +01:00

Merge pull request #3402 from academician/minus-integration

Add --pager=builtin using the Minus library
This commit is contained in:
Keith Hall
2025-09-20 08:43:47 +03:00
committed by GitHub
15 changed files with 273 additions and 31 deletions

View File

@@ -6,6 +6,7 @@
- Add paging to `--list-themes`, see PR #3239 (@einfachIrgendwer0815)
- Support negative relative line ranges, e.g. `bat -r :-10` / `bat -r='-10:'`, see #3068 (@ajesipow)
- Support context in line ranges, e.g. `bat -r 30::5` / `bat -r 30:40:5`, see #3345 (@cavanaug)
- Add built-in 'minus' pager, e.g. `bat --pager=builtin` see PR #3402 (@academician)
## Bugfixes

172
Cargo.lock generated
View File

@@ -135,6 +135,7 @@ dependencies = [
"indexmap",
"itertools 0.13.0",
"itertools 0.14.0",
"minus",
"nix",
"nu-ansi-term",
"once_cell",
@@ -339,6 +340,15 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
@@ -364,6 +374,31 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crossterm"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
"bitflags 2.6.0",
"crossterm_winapi",
"libc",
"mio 0.8.11",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]]
name = "darling"
version = "0.20.10"
@@ -983,6 +1018,33 @@ dependencies = [
"adler2",
]
[[package]]
name = "minus"
version = "5.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093bd0520d2a37943566a73750e6d44094dac75d66a978d1f0d97ffc78686832"
dependencies = [
"crossbeam-channel",
"crossterm",
"once_cell",
"parking_lot",
"regex",
"textwrap",
"thiserror 1.0.69",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.3"
@@ -1050,6 +1112,9 @@ name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
dependencies = [
"parking_lot_core",
]
[[package]]
name = "onig"
@@ -1428,6 +1493,36 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio 0.8.11",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.13.2"
@@ -1543,7 +1638,7 @@ dependencies = [
"cfg-if",
"libc",
"memchr",
"mio",
"mio 1.0.3",
"terminal-trx",
"windows-sys 0.59.0",
"xterm-color",
@@ -1576,6 +1671,15 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
[[package]]
name = "textwrap"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@@ -1932,6 +2036,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -1959,6 +2072,21 @@ dependencies = [
"windows-targets 0.53.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@@ -1991,6 +2119,12 @@ dependencies = [
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@@ -2003,6 +2137,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@@ -2015,6 +2155,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -2039,6 +2185,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@@ -2051,6 +2203,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@@ -2063,6 +2221,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@@ -2075,6 +2239,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View File

@@ -33,7 +33,7 @@ minimal-application = [
"wild",
]
git = ["git2"] # Support indicating git modifications
paging = ["shell-words", "grep-cli"] # Support applying a pager on the output
paging = [ "shell-words", "grep-cli", "minus"] # Support applying a pager on the output
lessopen = ["execute"] # Support $LESSOPEN preprocessor
build-assets = ["syntect/yaml-load", "syntect/plist-load", "regex", "walkdir"]
@@ -52,6 +52,10 @@ thiserror = "2.0"
wild = { version = "2.2", optional = true }
content_inspector = "0.2.4"
shell-words = { version = "1.1.0", optional = true }
minus = { version = "5.6", optional = true, features = [
"dynamic_output",
"search",
] }
unicode-width = "0.2.1"
globset = "0.4"
serde = "1.0"

View File

@@ -635,9 +635,11 @@ syntax, use (this mapping is already built in):
### Using a different pager
`bat` uses the pager that is specified in the `PAGER` environment variable. If this variable is not
set, `less` is used by default. If you want to use a different pager, you can either modify the
`PAGER` variable or set the `BAT_PAGER` environment variable to override what is specified in
`PAGER`.
set, `less` is used by default. You can also use bat's built-in pager with `--pager=builtin` or
by setting the `BAT_PAGER` environment variable to "builtin".
If you want to use a different pager, you can either modify the `PAGER` variable or set the
`BAT_PAGER` environment variable to override what is specified in `PAGER`.
>[!NOTE]
> If `PAGER` is `more` or `most`, `bat` will silently use `less` instead to ensure support for colors.

View File

@@ -106,6 +106,7 @@ set -l pager_opts '
less\ -FR\t
more\t
vimpager\t
builtin\t
'
set -l italic_text_opts '

View File

@@ -149,8 +149,9 @@ which pager is used, see the '\-\-pager' option. Possible values: *auto*, never,
\fB\-\-pager\fR <command>
.IP
Determine which pager is used. This option will override the PAGER and BAT_PAGER
environment variables. The default pager is 'less'. To control when the pager is used, see
the '\-\-paging' option. Example: '\-\-pager "less \fB\-RF\fR"'.
environment variables. The default pager is 'less'. If you provide '\-\-pager=builtin', use
the built-in 'minus' pager. To control when the pager is used, see the '\-\-paging' option.
Example: '\-\-pager "less \fB\-RF\fR"'.
Note: By default, if the pager is set to 'less' (and no command-line options are specified), 'bat' will pass the following command line options to the pager: '-R'/'--RAW-CONTROL-CHARS', '-F'/'--quit-if-one-screen' and '-X'/'--no-init'. The last option ('-X') is only used for 'less' versions older than 530. The '-R' option is needed to interpret ANSI colors correctly. The second option ('-F') instructs less to exit immediately if the output size is smaller than the vertical size of the terminal. This is convenient for small files because you do not have to press 'q' to quit the pager. The third option ('-X') is needed to fix a bug with the '--quit-if-one-screen' feature in old versions of 'less'. Unfortunately, it also breaks mouse-wheel support in 'less'. If you want to enable mouse-wheel scrolling on older versions of 'less', you can pass just '-R' (as in the example above, this will disable the quit-if-one-screen feature). For less 530 or newer, it should work out of the box.
.HP

View File

@@ -95,14 +95,15 @@ Options:
piped to another program, but you want to keep the colorization/decorations.
--paging <when>
Specify when to use the pager. To disable the pager, use --paging=never' or its
Specify when to use the pager. To disable the pager, use '--paging=never' or its
alias,'-P'. To disable the pager permanently, set BAT_PAGING to 'never'. To control which
pager is used, see the '--pager' option. Possible values: *auto*, never, always.
--pager <command>
Determine which pager is used. This option will override the PAGER and BAT_PAGER
environment variables. The default pager is 'less'. To control when the pager is used, see
the '--paging' option. Example: '--pager "less -RF"'.
environment variables. The default pager is 'less'. If you provide '--pager=builtin', use
the built-in 'minus' pager. To control when the pager is used, see the '--paging' option.
Example: '--pager "less -RF"'.
-m, --map-syntax <glob:syntax>
Map a glob pattern to an existing syntax name. The glob pattern is matched on the full

View File

@@ -14,7 +14,7 @@ fn main() {
controller
.run(
vec![input.into()],
Some(OutputHandle::FmtWrite(&mut buffer)),
Some(&mut OutputHandle::FmtWrite(&mut buffer)),
)
.unwrap();

View File

@@ -328,7 +328,7 @@ pub fn build_app(interactive_output: bool) -> Command {
.help("Specify when to use the pager, or use `-P` to disable (*auto*, never, always).")
.long_help(
"Specify when to use the pager. To disable the pager, use \
--paging=never' or its alias,'-P'. To disable the pager permanently, \
'--paging=never' or its alias,'-P'. To disable the pager permanently, \
set BAT_PAGING to 'never'. To control which pager is used, see the \
'--pager' option. Possible values: *auto*, never, always."
),
@@ -354,6 +354,7 @@ pub fn build_app(interactive_output: bool) -> Command {
.long_help(
"Determine which pager is used. This option will override the \
PAGER and BAT_PAGER environment variables. The default pager is 'less'. \
If you provide '--pager=builtin', use the built-in 'minus' pager. \
To control when the pager is used, see the '--paging' option. \
Example: '--pager \"less -RF\"'."
),

View File

@@ -16,7 +16,7 @@ use std::io::{BufReader, Write};
use std::path::Path;
use std::process;
use bat::output::{OutputHandle, OutputType};
use bat::output::OutputType;
use bat::theme::DetectColorScheme;
use nu_ansi_term::Color::Green;
use nu_ansi_term::Style;
@@ -229,10 +229,7 @@ pub fn list_themes(
)?;
config.theme = theme.to_string();
Controller::new(&config, &assets)
.run(
vec![theme_preview_file()],
Some(OutputHandle::IoWrite(&mut writer)),
)
.run(vec![theme_preview_file()], Some(&mut writer))
.ok();
writeln!(writer)?;
} else if config.loop_through {

View File

@@ -36,14 +36,18 @@ impl Controller<'_> {
}
}
pub fn run(&self, inputs: Vec<Input>, output_handle: Option<OutputHandle<'_>>) -> Result<bool> {
pub fn run(
&self,
inputs: Vec<Input>,
output_handle: Option<&mut OutputHandle<'_>>,
) -> Result<bool> {
self.run_with_error_handler(inputs, output_handle, default_error_handler)
}
pub fn run_with_error_handler(
&self,
inputs: Vec<Input>,
output_handle: Option<OutputHandle<'_>>,
output_handle: Option<&mut OutputHandle<'_>>,
mut handle_error: impl FnMut(&Error, &mut dyn Write),
) -> Result<bool> {
let mut output_type;
@@ -88,7 +92,7 @@ impl Controller<'_> {
let mut writer = match output_handle {
Some(OutputHandle::FmtWrite(w)) => OutputHandle::FmtWrite(w),
Some(OutputHandle::IoWrite(w)) => OutputHandle::IoWrite(w),
None => OutputHandle::IoWrite(output_type.handle()?),
None => output_type.handle()?,
};
let mut no_errors: bool = true;
let stderr = io::stderr();

View File

@@ -28,6 +28,9 @@ pub enum Error {
InvalidPagerValueBat,
#[error("{0}")]
Msg(String),
#[cfg(feature = "paging")]
#[error(transparent)]
MinusError(#[from] ::minus::MinusError),
#[cfg(feature = "lessopen")]
#[error(transparent)]
VarError(#[from] ::std::env::VarError),

View File

@@ -1,7 +1,9 @@
use std::fmt;
use std::io::{self, Write};
use std::io;
#[cfg(feature = "paging")]
use std::process::Child;
#[cfg(feature = "paging")]
use std::thread::{spawn, JoinHandle};
use crate::error::*;
#[cfg(feature = "paging")]
@@ -11,6 +13,36 @@ use crate::paging::PagingMode;
#[cfg(feature = "paging")]
use crate::wrapping::WrappingMode;
#[cfg(feature = "paging")]
pub struct BuiltinPager {
pager: minus::Pager,
handle: Option<JoinHandle<Result<()>>>,
}
#[cfg(feature = "paging")]
impl BuiltinPager {
fn new() -> Self {
let pager = minus::Pager::new();
let handle = {
let pager = pager.clone();
Some(spawn(move || {
minus::dynamic_paging(pager).map_err(Error::from)
}))
};
Self { pager, handle }
}
}
#[cfg(feature = "paging")]
impl std::fmt::Debug for BuiltinPager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BuiltinPager")
//.field("pager", &self.pager) /// minus::Pager doesn't implement fmt::Debug
.field("handle", &self.handle)
.finish()
}
}
#[cfg(feature = "paging")]
#[derive(Debug, PartialEq)]
enum SingleScreenAction {
@@ -22,6 +54,8 @@ enum SingleScreenAction {
pub enum OutputType {
#[cfg(feature = "paging")]
Pager(Child),
#[cfg(feature = "paging")]
BuiltinPager(BuiltinPager),
Stdout(io::Stdout),
}
@@ -64,6 +98,10 @@ impl OutputType {
return Err(Error::InvalidPagerValueBat);
}
if pager.kind == PagerKind::Builtin {
return Ok(OutputType::BuiltinPager(BuiltinPager::new()));
}
let resolved_path = match grep_cli::resolve_binary(&pager.bin) {
Ok(path) => path,
Err(_) => {
@@ -138,7 +176,7 @@ impl OutputType {
#[cfg(feature = "paging")]
pub(crate) fn is_pager(&self) -> bool {
matches!(self, OutputType::Pager(_))
matches!(self, OutputType::Pager(_) | OutputType::BuiltinPager(_))
}
#[cfg(not(feature = "paging"))]
@@ -146,14 +184,18 @@ impl OutputType {
false
}
pub fn handle(&mut self) -> Result<&mut dyn Write> {
pub fn handle<'a>(&'a mut self) -> Result<OutputHandle<'a>> {
Ok(match *self {
#[cfg(feature = "paging")]
OutputType::Pager(ref mut command) => command
OutputType::Pager(ref mut command) => OutputHandle::IoWrite(
command
.stdin
.as_mut()
.ok_or("Could not open stdin for pager")?,
OutputType::Stdout(ref mut handle) => handle,
),
#[cfg(feature = "paging")]
OutputType::BuiltinPager(ref mut pager) => OutputHandle::FmtWrite(&mut pager.pager),
OutputType::Stdout(ref mut handle) => OutputHandle::IoWrite(handle),
})
}
}
@@ -161,9 +203,17 @@ impl OutputType {
#[cfg(feature = "paging")]
impl Drop for OutputType {
fn drop(&mut self) {
if let OutputType::Pager(ref mut command) = *self {
match *self {
OutputType::Pager(ref mut command) => {
let _ = command.wait();
}
OutputType::BuiltinPager(ref mut pager) => {
if pager.handle.is_some() {
let _ = pager.handle.take().unwrap().join().unwrap();
}
}
OutputType::Stdout(_) => (),
}
}
}

View File

@@ -32,6 +32,9 @@ pub(crate) enum PagerKind {
/// most
Most,
/// builtin
Builtin,
/// A pager we don't know about
Unknown,
}
@@ -40,6 +43,10 @@ impl PagerKind {
fn from_bin(bin: &str) -> PagerKind {
use std::path::Path;
if bin == "builtin" {
return PagerKind::Builtin;
}
// Set to `less` by default on most Linux distros.
let pager_bin = Path::new(bin).file_stem();

View File

@@ -328,7 +328,7 @@ impl<'a> PrettyPrinter<'a> {
if let Some(mut w) = writer {
controller.run(
inputs.into_iter().map(|i| i.into()).collect(),
Some(OutputHandle::FmtWrite(&mut w)),
Some(&mut OutputHandle::FmtWrite(&mut w)),
)
} else {
controller.run(inputs.into_iter().map(|i| i.into()).collect(), None)