on 3dcf025 help sign in
bat/src/pager.rs
123 lines 3.4 kB history raw
1use shell_words::ParseError;
2use std::env;
4/// If we use a pager, this enum tells us from where we were told to use it.
5#[derive(Debug, PartialEq)]
6pub(crate) enum PagerSource {
7 /// From --config
8 Config,
10 /// From the env var BAT_PAGER
11 EnvVarBatPager,
13 /// From the env var PAGER
14 EnvVarPager,
16 /// No pager was specified, default is used
17 Default,
20/// We know about some pagers, for example 'less'. This is a list of all pagers we know about
21#[derive(Debug, PartialEq)]
22pub(crate) enum PagerKind {
23 /// bat
24 Bat,
26 /// less
27 Less,
29 /// more
30 More,
32 /// most
33 Most,
35 /// A pager we don't know about
36 Unknown,
39impl PagerKind {
40 fn from_bin(bin: &str) -> PagerKind {
41 use std::path::Path;
43 match Path::new(bin)
44 .file_stem()
45 .map(|s| s.to_string_lossy())
46 .as_deref()
47 {
48 Some("bat") => PagerKind::Bat,
49 Some("less") => PagerKind::Less,
50 Some("more") => PagerKind::More,
51 Some("most") => PagerKind::Most,
52 _ => PagerKind::Unknown,
53 }
54 }
57/// A pager such as 'less', and from where we got it.
58#[derive(Debug)]
59pub(crate) struct Pager {
60 /// The pager binary
61 pub bin: String,
63 /// The pager binary arguments (that we might tweak)
64 pub args: Vec<String>,
66 /// What pager this is
67 pub kind: PagerKind,
69 /// From where this pager comes
70 pub source: PagerSource,
73impl Pager {
74 fn new(bin: &str, args: &[String], kind: PagerKind, source: PagerSource) -> Pager {
75 Pager {
76 bin: String::from(bin),
77 args: args.to_vec(),
78 kind,
79 source,
80 }
81 }
84/// Returns what pager to use, after looking at both config and environment variables.
85pub(crate) fn get_pager(config_pager: Option<&str>) -> Result<Option<Pager>, ParseError> {
86 let bat_pager = env::var("BAT_PAGER");
87 let pager = env::var("PAGER");
89 let (cmd, source) = match (config_pager, &bat_pager, &pager) {
90 (Some(config_pager), _, _) => (config_pager, PagerSource::Config),
91 (_, Ok(bat_pager), _) => (bat_pager.as_str(), PagerSource::EnvVarBatPager),
92 (_, _, Ok(pager)) => (pager.as_str(), PagerSource::EnvVarPager),
93 _ => ("less", PagerSource::Default),
94 };
96 let parts = shell_words::split(cmd)?;
97 match parts.split_first() {
98 Some((bin, args)) => {
99 let kind = PagerKind::from_bin(bin);
101 let use_less_instead = match (&source, &kind) {
102 // 'more' and 'most' do not supports colors; automatically use 'less' instead
103 // if the problematic pager came from the generic PAGER env var
104 (PagerSource::EnvVarPager, PagerKind::More) => true,
105 (PagerSource::EnvVarPager, PagerKind::Most) => true,
107 // If PAGER=bat, silently use 'less' instead to prevent recursion ...
108 (PagerSource::EnvVarPager, PagerKind::Bat) => true,
110 // Never silently use less if BAT_PAGER or --pager has been specified
111 _ => false,
112 };
114 Ok(Some(if use_less_instead {
115 let no_args = vec![];
116 Pager::new("less", &no_args, PagerKind::Less, PagerSource::EnvVarPager)
117 } else {
118 Pager::new(bin, args, kind, source)
119 }))
120 }
121 None => Ok(None),
122 }