diff --git a/Cargo.toml b/Cargo.toml index 60b2d4e158..84cb103ac2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,24 +15,41 @@ exclude = [ build = "build.rs" edition = '2018' +[features] +default = ["application"] +# Feature required for bat the application. Should be disabled when depending on +# bat as a library. +application = [ + "atty", + "clap", + "dirs", + "git", + "lazy_static", + "liquid", + "paging", + "wild", +] +git = ["git2"] # Support indicating git modifications +paging = ["shell-words"] # Support applying a pager on the output + [dependencies] -atty = "0.2.14" +atty = { version = "0.2.14", optional = true } ansi_term = "^0.12.1" ansi_colours = "^1.0" console = "0.10" -dirs = "2.0" -lazy_static = "1.4" -wild = "2.0" +dirs = { version = "2.0", optional = true } +lazy_static = { version = "1.4", optional = true } +wild = { version = "2.0", optional = true } content_inspector = "0.2.4" encoding = "0.2" -shell-words = "0.1.0" +shell-words = { version = "0.1.0", optional = true } unicode-width = "0.1.7" globset = "0.4" [dependencies.git2] version = "0.13" +optional = true default-features = false -features = [] [dependencies.syntect] version = "3.3.0" @@ -41,22 +58,22 @@ features = ["parsing", "yaml-load", "dump-load", "dump-create"] [dependencies.clap] version = "2.33" +optional = true default-features = false features = ["suggestions", "color", "wrap_help"] [dependencies.error-chain] version = "0.12" default-features = false -features = [] [dev-dependencies] tempdir = "0.3" assert_cmd = "0.12.0" [build-dependencies] -clap = "2.33" -liquid = "0.20" -lazy_static = "1.4" +clap = { version = "2.33", optional = true } +liquid = { version = "0.20", optional = true } +lazy_static = { version = "1.4", optional = true } [profile.release] lto = true diff --git a/build.rs b/build.rs index bff88e9f03..074caaff76 100644 --- a/build.rs +++ b/build.rs @@ -1,38 +1,42 @@ // TODO: Re-enable generation of shell completion files (below) when clap 3 is out. // For more details, see https://github.com/sharkdp/bat/issues/372 -#[macro_use] -extern crate lazy_static; -extern crate liquid; - -use std::error::Error; -use std::fs; -use std::path::Path; - -// Read environment variables. -lazy_static! { - pub static ref PROJECT_NAME: &'static str = option_env!("PROJECT_NAME").unwrap_or("bat"); - pub static ref PROJECT_VERSION: &'static str = option_env!("CARGO_PKG_VERSION").unwrap(); - pub static ref EXECUTABLE_NAME: &'static str = option_env!("PROJECT_EXECUTABLE") - .or(option_env!("PROJECT_NAME")) - .unwrap_or("bat"); -} - -/// Generates a file from a liquid template. -fn template( - variables: &liquid::Object, - in_file: &str, - out_file: impl AsRef, -) -> Result<(), Box> { - let template = liquid::ParserBuilder::with_stdlib() - .build()? - .parse(&fs::read_to_string(in_file)?)?; - - fs::write(out_file, template.render(variables)?)?; - Ok(()) -} +// For bat-as-a-library, no build script is required. The build script is for +// the manpage and completions, which are only relevant to the bat application. +#[cfg(not(feature = "application"))] +fn main() {} + +#[cfg(feature = "application")] +fn main() -> Result<(), Box> { + use std::error::Error; + use std::fs; + use std::path::Path; + + use lazy_static::lazy_static; + + // Read environment variables. + lazy_static! { + static ref PROJECT_NAME: &'static str = option_env!("PROJECT_NAME").unwrap_or("bat"); + static ref PROJECT_VERSION: &'static str = option_env!("CARGO_PKG_VERSION").unwrap(); + static ref EXECUTABLE_NAME: &'static str = option_env!("PROJECT_EXECUTABLE") + .or(option_env!("PROJECT_NAME")) + .unwrap_or("bat"); + } + + /// Generates a file from a liquid template. + fn template( + variables: &liquid::Object, + in_file: &str, + out_file: impl AsRef, + ) -> Result<(), Box> { + let template = liquid::ParserBuilder::with_stdlib() + .build()? + .parse(&fs::read_to_string(in_file)?)?; + + fs::write(out_file, template.render(variables)?)?; + Ok(()) + } -fn main() -> Result<(), Box> { let variables = liquid::object!({ "PROJECT_NAME": PROJECT_NAME.to_owned(), "PROJECT_EXECUTABLE": EXECUTABLE_NAME.to_owned(), diff --git a/ci/script.bash b/ci/script.bash index db608c16d6..c53d62e296 100755 --- a/ci/script.bash +++ b/ci/script.bash @@ -12,3 +12,9 @@ if [[ $TARGET != arm-unknown-linux-gnueabihf ]] && [[ $TARGET != aarch64-unknown # Run 'bat' on its own source code and the README cargo run --target "$TARGET" -- src/bin/bat/main.rs README.md --paging=never fi + +# Check bat-as-a-library, which has a smaller set of dependencies +cargo check --target "$TARGET" --verbose --lib --no-default-features +cargo check --target "$TARGET" --verbose --lib --no-default-features --features git +cargo check --target "$TARGET" --verbose --lib --no-default-features --features paging +cargo check --target "$TARGET" --verbose --lib --no-default-features --features git,paging diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index f1649b3fd1..56988b17c6 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,13 +9,9 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_var}, }; use clap::ArgMatches; -use wild; use console::Term; -#[cfg(windows)] -use ansi_term; - use bat::{ config::{ Config, HighlightedLineRanges, InputFile, LineRange, LineRanges, MappingTarget, OutputWrap, diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index c2505ca945..c7344991b6 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -1,4 +1,4 @@ -use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, SubCommand}; +use clap::{crate_name, crate_version, App as ClapApp, AppSettings, Arg, ArgGroup, SubCommand}; use std::path::Path; pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> { diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 25c14d3b97..8595ad5b7c 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -4,8 +4,6 @@ use std::fs; use std::io::{self, Write}; use std::path::PathBuf; -use shell_words; - use crate::directories::PROJECT_DIRS; pub fn config_file() -> PathBuf { diff --git a/src/bin/bat/directories.rs b/src/bin/bat/directories.rs index e57a58c0f3..8f0f261d0e 100644 --- a/src/bin/bat/directories.rs +++ b/src/bin/bat/directories.rs @@ -1,7 +1,6 @@ use std::env; use std::path::{Path, PathBuf}; -use dirs; use lazy_static::lazy_static; /// Wrapper for 'dirs' that treats MacOS more like Linux, by following the XDG specification. diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 20130118c0..15cc37fa7f 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -1,11 +1,6 @@ // `error_chain!` can recurse deeply #![recursion_limit = "1024"] -#[macro_use] -extern crate clap; - -extern crate dirs as dirs_rs; - mod app; mod assets; mod clap_app; diff --git a/src/config.rs b/src/config.rs index 6a0c25303a..b0544acd3e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,12 +5,14 @@ pub use crate::syntax_mapping::{MappingTarget, SyntaxMapping}; pub use crate::wrap::OutputWrap; #[derive(Debug, Clone, Copy, PartialEq)] +#[cfg(feature = "paging")] pub enum PagingMode { Always, QuitIfOneScreen, Never, } +#[cfg(feature = "paging")] impl Default for PagingMode { fn default() -> Self { PagingMode::Never @@ -51,6 +53,7 @@ pub struct Config<'a> { pub output_wrap: OutputWrap, /// Pager or STDOUT + #[cfg(feature = "paging")] pub paging_mode: PagingMode, /// Specifies the lines that should be printed diff --git a/src/controller.rs b/src/controller.rs index 90e465bd04..fd874759d2 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,8 +1,9 @@ use std::io::{self, Write}; -use std::path::Path; use crate::assets::HighlightingAssets; -use crate::config::{Config, PagingMode}; +use crate::config::Config; +#[cfg(feature = "paging")] +use crate::config::PagingMode; use crate::errors::*; use crate::inputfile::{InputFile, InputFileReader}; use crate::line_range::{LineRanges, RangeCheckResult}; @@ -24,22 +25,34 @@ impl<'b> Controller<'b> { } pub fn run_with_error_handler(&self, handle_error: impl Fn(&Error)) -> Result { - // Do not launch the pager if NONE of the input files exist - let mut paging_mode = self.config.paging_mode; - if self.config.paging_mode != PagingMode::Never { - let call_pager = self.config.files.iter().any(|file| { - if let InputFile::Ordinary(path) = file { - return Path::new(path).exists(); - } else { - return true; + let mut output_type; + + #[cfg(feature = "paging")] + { + use std::path::Path; + + // Do not launch the pager if NONE of the input files exist + let mut paging_mode = self.config.paging_mode; + if self.config.paging_mode != PagingMode::Never { + let call_pager = self.config.files.iter().any(|file| { + if let InputFile::Ordinary(path) = file { + return Path::new(path).exists(); + } else { + return true; + } + }); + if !call_pager { + paging_mode = PagingMode::Never; } - }); - if !call_pager { - paging_mode = PagingMode::Never; } + output_type = OutputType::from_mode(paging_mode, self.config.pager)?; + } + + #[cfg(not(feature = "paging"))] + { + output_type = OutputType::stdout(); } - let mut output_type = OutputType::from_mode(paging_mode, self.config.pager)?; let writer = output_type.handle()?; let mut no_errors: bool = true; diff --git a/src/decorations.rs b/src/decorations.rs index 787a847b4e..1b44d05ae4 100644 --- a/src/decorations.rs +++ b/src/decorations.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "git")] use crate::diff::LineChange; use crate::printer::{Colors, InteractivePrinter}; use ansi_term::Style; @@ -68,6 +69,7 @@ impl Decoration for LineNumberDecoration { } } +#[cfg(feature = "git")] pub struct LineChangesDecoration { cached_none: DecorationText, cached_added: DecorationText, @@ -76,6 +78,7 @@ pub struct LineChangesDecoration { cached_modified: DecorationText, } +#[cfg(feature = "git")] impl LineChangesDecoration { #[inline] fn generate_cached(style: Style, text: &str) -> DecorationText { @@ -96,6 +99,7 @@ impl LineChangesDecoration { } } +#[cfg(feature = "git")] impl Decoration for LineChangesDecoration { fn generate( &self, diff --git a/src/diff.rs b/src/diff.rs index bf0c1f3067..bd08468a86 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "git")] + use std::collections::HashMap; use std::ffi::OsStr; use std::fs; diff --git a/src/errors.rs b/src/errors.rs index a0f7e5eb12..91b890d01e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -2,7 +2,7 @@ use error_chain::error_chain; error_chain! { foreign_links { - Clap(::clap::Error); + Clap(::clap::Error) #[cfg(feature = "application")]; Io(::std::io::Error); SyntectError(::syntect::LoadingError); ParseIntError(::std::num::ParseIntError); diff --git a/src/less.rs b/src/less.rs index f74293334b..aaab849f29 100644 --- a/src/less.rs +++ b/src/less.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "paging")] + use std::process::Command; pub fn retrieve_less_version() -> Option { diff --git a/src/lib.rs b/src/lib.rs index 8178f497be..404ad49cd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,6 @@ // `error_chain!` can recurse deeply #![recursion_limit = "1024"] -extern crate ansi_term; -extern crate atty; -extern crate console; -extern crate content_inspector; -extern crate dirs as dirs_rs; -extern crate encoding; -extern crate git2; -extern crate shell_words; -extern crate syntect; -extern crate wild; - pub(crate) mod assets; pub mod config; pub(crate) mod controller; diff --git a/src/output.rs b/src/output.rs index 3bad301bd5..15f29e432f 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,22 +1,22 @@ -use std::env; -use std::ffi::OsString; use std::io::{self, Write}; -use std::path::PathBuf; -use std::process::{Child, Command, Stdio}; - -use shell_words; +#[cfg(feature = "paging")] +use std::process::Child; +#[cfg(feature = "paging")] use crate::config::PagingMode; use crate::errors::*; +#[cfg(feature = "paging")] use crate::less::retrieve_less_version; #[derive(Debug)] pub enum OutputType { + #[cfg(feature = "paging")] Pager(Child), Stdout(io::Stdout), } impl OutputType { + #[cfg(feature = "paging")] pub fn from_mode(mode: PagingMode, pager: Option<&str>) -> Result { use self::PagingMode::*; Ok(match mode { @@ -27,7 +27,13 @@ impl OutputType { } /// Try to launch the pager. Fall back to stdout in case of errors. + #[cfg(feature = "paging")] fn try_pager(quit_if_one_screen: bool, pager_from_config: Option<&str>) -> Result { + use std::env; + use std::ffi::OsString; + use std::path::PathBuf; + use std::process::{Command, Stdio}; + let mut replace_arguments_to_less = false; let pager_from_env = match (env::var("BAT_PAGER"), env::var("PAGER")) { @@ -110,12 +116,13 @@ impl OutputType { } } - fn stdout() -> Self { + pub(crate) fn stdout() -> Self { OutputType::Stdout(io::stdout()) } pub fn handle(&mut self) -> Result<&mut dyn Write> { Ok(match *self { + #[cfg(feature = "paging")] OutputType::Pager(ref mut command) => command .stdin .as_mut() @@ -125,6 +132,7 @@ impl OutputType { } } +#[cfg(feature = "paging")] impl Drop for OutputType { fn drop(&mut self) { if let OutputType::Pager(ref mut command) = *self { diff --git a/src/printer.rs b/src/printer.rs index 3924cd62e4..b269219c7d 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -21,11 +21,11 @@ use unicode_width::UnicodeWidthChar; use crate::assets::HighlightingAssets; use crate::config::Config; -use crate::decorations::{ - Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration, -}; -use crate::diff::get_git_diff; -use crate::diff::LineChanges; +use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration}; +#[cfg(feature = "git")] +use crate::decorations::LineChangesDecoration; +#[cfg(feature = "git")] +use crate::diff::{get_git_diff, LineChanges}; use crate::errors::*; use crate::inputfile::{InputFile, InputFileReader}; use crate::line_range::RangeCheckResult; @@ -100,6 +100,7 @@ pub struct InteractivePrinter<'a> { panel_width: usize, ansi_prefix_sgr: String, content_type: Option, + #[cfg(feature = "git")] pub line_changes: Option, highlighter: Option>, syntax_set: &'a SyntaxSet, @@ -130,8 +131,11 @@ impl<'a> InteractivePrinter<'a> { decorations.push(Box::new(LineNumberDecoration::new(&colors))); } - if config.style_components.changes() { - decorations.push(Box::new(LineChangesDecoration::new(&colors))); + #[cfg(feature = "git")] + { + if config.style_components.changes() { + decorations.push(Box::new(LineChangesDecoration::new(&colors))); + } } let mut panel_width: usize = @@ -153,6 +157,7 @@ impl<'a> InteractivePrinter<'a> { panel_width = 0; } + #[cfg(feature = "git")] let mut line_changes = None; let highlighter = if reader @@ -162,14 +167,14 @@ impl<'a> InteractivePrinter<'a> { None } else { // Get the Git modifications - line_changes = if config.style_components.changes() { - match file { - InputFile::Ordinary(filename) => get_git_diff(filename), - _ => None, + #[cfg(feature = "git")] + { + if config.style_components.changes() { + if let InputFile::Ordinary(filename) = file { + line_changes = get_git_diff(filename); + } } - } else { - None - }; + } // Determine the type of syntax for highlighting let syntax = assets.get_syntax(config.language, file, reader, &config.syntax_mapping); @@ -183,6 +188,7 @@ impl<'a> InteractivePrinter<'a> { decorations, content_type: reader.content_type, ansi_prefix_sgr: String::new(), + #[cfg(feature = "git")] line_changes, highlighter, syntax_set: &assets.syntax_set, diff --git a/src/style.rs b/src/style.rs index a906553af5..1821e4956a 100644 --- a/src/style.rs +++ b/src/style.rs @@ -68,6 +68,7 @@ impl StyleComponents { StyleComponents(components.iter().cloned().collect()) } + #[cfg(feature = "git")] pub fn changes(&self) -> bool { self.0.contains(&StyleComponent::Changes) } diff --git a/src/terminal.rs b/src/terminal.rs index 8c3766c50d..981bf3b23a 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,5 +1,3 @@ -extern crate ansi_colours; - use ansi_term::Colour::{Fixed, RGB}; use ansi_term::{self, Style}; diff --git a/tests/tester.rs b/tests/tester.rs index a761babe1b..9c52459fe3 100644 --- a/tests/tester.rs +++ b/tests/tester.rs @@ -4,13 +4,11 @@ use std::io::Read; use std::path::{Path, PathBuf}; use std::process::Command; -extern crate tempdir; -use self::tempdir::TempDir; +use tempdir::TempDir; -extern crate git2; -use self::git2::build::CheckoutBuilder; -use self::git2::Repository; -use self::git2::Signature; +use git2::build::CheckoutBuilder; +use git2::Repository; +use git2::Signature; pub struct BatTester { /// Temporary working directory