diff --git a/Cargo.lock b/Cargo.lock index ba08457e..8fab3ceb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -721,6 +722,11 @@ name = "vcpkg" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.4" @@ -883,6 +889,7 @@ dependencies = [ "checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" "checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35" diff --git a/Cargo.toml b/Cargo.toml index 95e5f3ec..4d75e480 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ exclude = [ "assets/syntaxes/*", "assets/themes/*", ] +build = "build.rs" [dependencies] atty = "0.2.2" @@ -43,3 +44,6 @@ features = [] [dev-dependencies] tempdir = "0.3" + +[build-dependencies] +clap = "2.32" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..78085579 --- /dev/null +++ b/build.rs @@ -0,0 +1,26 @@ +#[macro_use] +extern crate clap; + +use clap::Shell; +use std::fs; + +include!("src/clap_app.rs"); + +const BIN_NAME: &str = "bat"; + +fn main() { + let outdir = std::env::var_os("SHELL_COMPLETIONS_DIR").or(std::env::var_os("OUT_DIR")); + + let outdir = match outdir { + None => return, + Some(outdir) => outdir, + }; + + fs::create_dir_all(&outdir).unwrap(); + + let mut app = build_app(true); + app.gen_completions(BIN_NAME, Shell::Bash, &outdir); + app.gen_completions(BIN_NAME, Shell::Fish, &outdir); + app.gen_completions(BIN_NAME, Shell::Zsh, &outdir); + app.gen_completions(BIN_NAME, Shell::PowerShell, &outdir); +} diff --git a/ci/before_deploy.bash b/ci/before_deploy.bash index 16cb261c..daf4f7a6 100755 --- a/ci/before_deploy.bash +++ b/ci/before_deploy.bash @@ -27,6 +27,7 @@ pack() { # create a "staging" directory mkdir "$tempdir/$package_name" + mkdir "$tempdir/$package_name/autocomplete" # copying the main binary cp "target/$TARGET/release/$PROJECT_NAME" "$tempdir/$package_name/" @@ -38,6 +39,11 @@ pack() { cp LICENSE-MIT "$tempdir/$package_name" cp LICENSE-APACHE "$tempdir/$package_name" + # various autocomplete + cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/"$PROJECT_NAME".bash "$tempdir/$package_name/autocomplete/${PROJECT_NAME}.bash-completion" + cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/"$PROJECT_NAME".fish "$tempdir/$package_name/autocomplete" + cp target/"$TARGET"/release/build/"$PROJECT_NAME"-*/out/_"$PROJECT_NAME" "$tempdir/$package_name/autocomplete" + # archiving pushd "$tempdir" tar czf "$out_dir/$package_name.tar.gz" "$package_name"/* diff --git a/ci/before_deploy.ps1 b/ci/before_deploy.ps1 index 32966198..b0c625f6 100644 --- a/ci/before_deploy.ps1 +++ b/ci/before_deploy.ps1 @@ -11,6 +11,7 @@ Set-Location $STAGE $ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip" Copy-Item "$SRC_DIR\target\release\bat.exe" '.\' +Copy-Item $SRC_DIR\target\release\build\bat-*\out\_bat.ps1 '.\' # readme and license Copy-Item $SRC_DIR\README.md '.\' diff --git a/src/app.rs b/src/app.rs index b645e01d..a365658c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,11 +1,11 @@ use std::collections::HashSet; use std::env; -use std::path::Path; use std::str::FromStr; use atty::{self, Stream}; -use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand}; +use clap::ArgMatches; +use clap_app; use console::Term; @@ -102,259 +102,7 @@ impl App { } fn matches(interactive_output: bool) -> ArgMatches<'static> { - let clap_color_setting = if interactive_output { - AppSettings::ColoredHelp - } else { - AppSettings::ColorNever - }; - - // Check if the current directory contains a file name cache, if it does - // do not make the arguements for subcommand 'cache' required. - let arg_group_required = !Path::new("cache").exists(); - - ClapApp::new(crate_name!()) - .version(crate_version!()) - .global_setting(clap_color_setting) - .global_setting(AppSettings::DeriveDisplayOrder) - .global_setting(AppSettings::UnifiedHelpMessage) - .global_setting(AppSettings::NextLineHelp) - .setting(AppSettings::InferSubcommands) - .setting(AppSettings::ArgsNegateSubcommands) - .setting(AppSettings::DisableHelpSubcommand) - .setting(AppSettings::VersionlessSubcommands) - .max_term_width(90) - .about( - "A cat(1) clone with wings.\n\n\ - Use '--help' instead of '-h' to see a more detailed version of the help text.", - ).long_about("A cat(1) clone with syntax highlighting and Git integration.") - .arg( - Arg::with_name("FILE") - .help("File(s) to print / concatenate. Use '-' for standard input.") - .long_help( - "File(s) to print / concatenate. Use a dash ('-') or no argument at all \ - to read from standard input.", - ).multiple(true) - .empty_values(false), - ) - .arg( - Arg::with_name("language") - .short("l") - .long("language") - .overrides_with("language") - .help("Set the language for syntax highlighting.") - .long_help( - "Explicitly set the language for syntax highlighting. The language can be \ - specified as a name (like 'C++' or 'LaTeX') or possible file extension \ - (like 'cpp', 'hpp' or 'md'). Use '--list-languages' to show all supported \ - language names and file extensions." - ).takes_value(true), - ).arg( - Arg::with_name("list-languages") - .long("list-languages") - .conflicts_with("list-themes") - .help("Display all supported languages.") - .long_help("Display a list of supported languages for syntax highlighting."), - ).arg( - Arg::with_name("theme") - .long("theme") - .overrides_with("theme") - .takes_value(true) - .help("Set the color theme for syntax highlighting.") - .long_help( - "Set the theme for syntax highlighting. Use '--list-themes' to \ - see all available themes. To set a default theme, export the \ - BAT_THEME environment variable (e.g.: export \ - BAT_THEME=\"TwoDark\").", - ), - ).arg( - Arg::with_name("list-themes") - .long("list-themes") - .help("Display all supported highlighting themes.") - .long_help("Display a list of supported themes for syntax highlighting."), - ).arg( - Arg::with_name("style") - .long("style") - .value_name("style-components") - .use_delimiter(true) - .takes_value(true) - .possible_values(&[ - "auto", "full", "plain", "changes", "header", "grid", "numbers", - ]) - .help("Comma-separated list of style elements to display.") - .long_help( - "Configure which elements (line numbers, file headers, grid \ - borders, Git modifications, ..) to display in addition to the \ - file contents. The argument is a comma-separated list of \ - components to display (e.g. 'numbers,changes,grid') or a \ - pre-defined style ('full'). To set a default theme, export the \ - BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"numbers\").", - ), - ).arg( - Arg::with_name("plain") - .overrides_with("plain") - .short("p") - .long("plain") - .conflicts_with("style") - .conflicts_with("number") - .help("Show plain style (alias for '--style=plain').") - .long_help( - "Only show plain style, no decorations. This is an alias for \ - '--style=plain'", - ), - ).arg( - Arg::with_name("number") - .long("number") - .overrides_with("number") - .short("n") - .conflicts_with("style") - .help("Show line numbers (alias for '--style=numbers').") - .long_help( - "Only show line numbers, no other decorations. This is an alias for \ - '--style=numbers'", - ), - ).arg( - Arg::with_name("line-range") - .long("line-range") - .overrides_with("line-range") - .takes_value(true) - .value_name("N:M") - .help("Only print the lines from N to M.") - .long_help( - "Only print the specified range of lines for each file. \ - For example:\n \ - '--line-range 30:40' prints lines 30 to 40\n \ - '--line-range :40' prints lines 1 to 40\n \ - '--line-range 40:' prints lines 40 to the end of the file", - ), - ).arg( - Arg::with_name("color") - .long("color") - .overrides_with("color") - .takes_value(true) - .value_name("when") - .possible_values(&["auto", "never", "always"]) - .default_value("auto") - .help("When to use colors.") - .long_help("Specify when to use colored output. The automatic mode \ - only enables colors if an interactive terminal is detected."), - ).arg( - Arg::with_name("decorations") - .long("decorations") - .overrides_with("decorations") - .takes_value(true) - .value_name("when") - .possible_values(&["auto", "never", "always"]) - .default_value("auto") - .help("When to show the decorations specified by '--style'.") - .long_help("Specify when to use the decorations that have been specified \ - via '--style'. The automatic mode only enables decorations if \ - an interactive terminal is detected."), - ).arg( - Arg::with_name("paging") - .long("paging") - .overrides_with("paging") - .takes_value(true) - .value_name("when") - .possible_values(&["auto", "never", "always"]) - .default_value("auto") - .help("Specify when to use the pager.") - .long_help("Specify when to use the pager. To control which pager \ - is used, set the PAGER or BAT_PAGER environment \ - variables (the latter takes precedence). The default \ - pager is 'less'. To disable the pager permanently, set \ - BAT_PAGER to an empty string."), - ).arg( - Arg::with_name("wrap") - .long("wrap") - .overrides_with("wrap") - .takes_value(true) - .value_name("mode") - .possible_values(&["auto", "never", "character"]) - .default_value("auto") - .help("Specify the text-wrapping mode.") - .long_help("Specify the text-wrapping mode."), - ).arg( - Arg::with_name("unbuffered") - .short("u") - .hidden_short_help(true) - .long_help( - "This option exists for POSIX-compliance reasons ('u' is for \ - 'unbuffered'). The output is always unbuffered - this option \ - is simply ignored.", - ), - ).arg( - Arg::with_name("tabs") - .long("tabs") - .takes_value(true) - .value_name("tabs") - .validator( - |t| t.parse::() - .map_err(|_t| "must be a number") - .map(|_t| ()) // Convert to Result<(), &str> - .map_err(|e| e.to_string()) // Convert to Result<(), String> - ) - .help("Sets the tab width.") - .long_help("Sets the tab width. Use a width of 0 to pass tabs through \ - directly"), - ).arg( - Arg::with_name("terminal-width") - .long("terminal-width") - .takes_value(true) - .value_name("width") - .hidden(true) - .help("Set the width of the terminal"), - ).subcommand( - SubCommand::with_name("cache") - .about("Modify the syntax-definition and theme cache") - .arg( - Arg::with_name("init") - .long("init") - .short("i") - .help("Initialize the syntax/theme cache.") - .long_help( - "Initialize the syntax/theme cache by loading from the \ - source directory (default: the configuration directory).", - ), - ).arg( - Arg::with_name("clear") - .long("clear") - .short("c") - .help("Remove the cached syntax definitions and themes."), - ).arg( - Arg::with_name("config-dir") - .long("config-dir") - .short("d") - .help("Show bat's configuration directory."), - ).group( - ArgGroup::with_name("cache-actions") - .args(&["init", "clear", "config-dir"]) - .required(arg_group_required), - ).arg( - Arg::with_name("source") - .long("source") - .requires("init") - .takes_value(true) - .value_name("dir") - .help("Use a different directory to load syntaxes and themes from."), - ).arg( - Arg::with_name("target") - .long("target") - .requires("init") - .takes_value(true) - .value_name("dir") - .help( - "Use a different directory to store the cached syntax and theme set.", - ), - ).arg( - Arg::with_name("blank") - .long("blank") - .requires("init") - .help("Create completely new syntax and theme sets \ - (instead of appending to the default sets).") - ), - ).help_message("Print this help message.") - .version_message("Show version information.") - .get_matches() + clap_app::build_app(interactive_output).get_matches() } pub fn config(&self) -> Result { diff --git a/src/clap_app.rs b/src/clap_app.rs new file mode 100644 index 00000000..5d97ef0e --- /dev/null +++ b/src/clap_app.rs @@ -0,0 +1,289 @@ +use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, SubCommand}; +use std::path::Path; + +pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> { + let clap_color_setting = if interactive_output { + AppSettings::ColoredHelp + } else { + AppSettings::ColorNever + }; + + // Check if the current directory contains a file name cache, if it does + // do not make the arguements for subcommand 'cache' required. + let arg_group_required = !Path::new("cache").exists(); + + ClapApp::new(crate_name!()) + .version(crate_version!()) + .global_setting(clap_color_setting) + .global_setting(AppSettings::DeriveDisplayOrder) + .global_setting(AppSettings::UnifiedHelpMessage) + .global_setting(AppSettings::NextLineHelp) + .setting(AppSettings::InferSubcommands) + .setting(AppSettings::ArgsNegateSubcommands) + .setting(AppSettings::DisableHelpSubcommand) + .setting(AppSettings::VersionlessSubcommands) + .max_term_width(90) + .about( + "A cat(1) clone with wings.\n\n\ + Use '--help' instead of '-h' to see a more detailed version of the help text.", + ) + .long_about("A cat(1) clone with syntax highlighting and Git integration.") + .arg( + Arg::with_name("FILE") + .help("File(s) to print / concatenate. Use '-' for standard input.") + .long_help( + "File(s) to print / concatenate. Use a dash ('-') or no argument at all \ + to read from standard input.", + ) + .multiple(true) + .empty_values(false), + ) + .arg( + Arg::with_name("language") + .short("l") + .long("language") + .overrides_with("language") + .help("Set the language for syntax highlighting.") + .long_help( + "Explicitly set the language for syntax highlighting. The language can be \ + specified as a name (like 'C++' or 'LaTeX') or possible file extension \ + (like 'cpp', 'hpp' or 'md'). Use '--list-languages' to show all supported \ + language names and file extensions.", + ) + .takes_value(true), + ) + .arg( + Arg::with_name("list-languages") + .long("list-languages") + .conflicts_with("list-themes") + .help("Display all supported languages.") + .long_help("Display a list of supported languages for syntax highlighting."), + ) + .arg( + Arg::with_name("theme") + .long("theme") + .overrides_with("theme") + .takes_value(true) + .help("Set the color theme for syntax highlighting.") + .long_help( + "Set the theme for syntax highlighting. Use '--list-themes' to \ + see all available themes. To set a default theme, export the \ + BAT_THEME environment variable (e.g.: export \ + BAT_THEME=\"TwoDark\").", + ), + ) + .arg( + Arg::with_name("list-themes") + .long("list-themes") + .help("Display all supported highlighting themes.") + .long_help("Display a list of supported themes for syntax highlighting."), + ) + .arg( + Arg::with_name("style") + .long("style") + .value_name("style-components") + .use_delimiter(true) + .takes_value(true) + .possible_values(&[ + "auto", "full", "plain", "changes", "header", "grid", "numbers", + ]) + .help("Comma-separated list of style elements to display.") + .long_help( + "Configure which elements (line numbers, file headers, grid \ + borders, Git modifications, ..) to display in addition to the \ + file contents. The argument is a comma-separated list of \ + components to display (e.g. 'numbers,changes,grid') or a \ + pre-defined style ('full'). To set a default theme, export the \ + BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"numbers\").", + ), + ) + .arg( + Arg::with_name("plain") + .overrides_with("plain") + .short("p") + .long("plain") + .conflicts_with("style") + .conflicts_with("number") + .help("Show plain style (alias for '--style=plain').") + .long_help( + "Only show plain style, no decorations. This is an alias for \ + '--style=plain'", + ), + ) + .arg( + Arg::with_name("number") + .long("number") + .overrides_with("number") + .short("n") + .conflicts_with("style") + .help("Show line numbers (alias for '--style=numbers').") + .long_help( + "Only show line numbers, no other decorations. This is an alias for \ + '--style=numbers'", + ), + ) + .arg( + Arg::with_name("line-range") + .long("line-range") + .overrides_with("line-range") + .takes_value(true) + .value_name("N:M") + .help("Only print the lines from N to M.") + .long_help( + "Only print the specified range of lines for each file. \ + For example:\n \ + '--line-range 30:40' prints lines 30 to 40\n \ + '--line-range :40' prints lines 1 to 40\n \ + '--line-range 40:' prints lines 40 to the end of the file", + ), + ) + .arg( + Arg::with_name("color") + .long("color") + .overrides_with("color") + .takes_value(true) + .value_name("when") + .possible_values(&["auto", "never", "always"]) + .default_value("auto") + .help("When to use colors.") + .long_help( + "Specify when to use colored output. The automatic mode \ + only enables colors if an interactive terminal is detected.", + ), + ) + .arg( + Arg::with_name("decorations") + .long("decorations") + .overrides_with("decorations") + .takes_value(true) + .value_name("when") + .possible_values(&["auto", "never", "always"]) + .default_value("auto") + .help("When to show the decorations specified by '--style'.") + .long_help( + "Specify when to use the decorations that have been specified \ + via '--style'. The automatic mode only enables decorations if \ + an interactive terminal is detected.", + ), + ) + .arg( + Arg::with_name("paging") + .long("paging") + .overrides_with("paging") + .takes_value(true) + .value_name("when") + .possible_values(&["auto", "never", "always"]) + .default_value("auto") + .help("Specify when to use the pager.") + .long_help( + "Specify when to use the pager. To control which pager \ + is used, set the PAGER or BAT_PAGER environment \ + variables (the latter takes precedence). The default \ + pager is 'less'. To disable the pager permanently, set \ + BAT_PAGER to an empty string.", + ), + ) + .arg( + Arg::with_name("wrap") + .long("wrap") + .overrides_with("wrap") + .takes_value(true) + .value_name("mode") + .possible_values(&["auto", "never", "character"]) + .default_value("auto") + .help("Specify the text-wrapping mode.") + .long_help("Specify the text-wrapping mode."), + ) + .arg( + Arg::with_name("unbuffered") + .short("u") + .hidden_short_help(true) + .long_help( + "This option exists for POSIX-compliance reasons ('u' is for \ + 'unbuffered'). The output is always unbuffered - this option \ + is simply ignored.", + ), + ) + .arg( + Arg::with_name("tabs") + .long("tabs") + .takes_value(true) + .value_name("tabs") + .validator( + |t| { + t.parse::() + .map_err(|_t| "must be a number") + .map(|_t| ()) // Convert to Result<(), &str> + .map_err(|e| e.to_string()) + }, // Convert to Result<(), String> + ) + .help("Sets the tab width.") + .long_help( + "Sets the tab width. Use a width of 0 to pass tabs through \ + directly", + ), + ) + .arg( + Arg::with_name("terminal-width") + .long("terminal-width") + .takes_value(true) + .value_name("width") + .hidden(true) + .help("Set the width of the terminal"), + ) + .subcommand( + SubCommand::with_name("cache") + .about("Modify the syntax-definition and theme cache") + .arg( + Arg::with_name("init") + .long("init") + .short("i") + .help("Initialize the syntax/theme cache.") + .long_help( + "Initialize the syntax/theme cache by loading from the \ + source directory (default: the configuration directory).", + ), + ) + .arg( + Arg::with_name("clear") + .long("clear") + .short("c") + .help("Remove the cached syntax definitions and themes."), + ) + .arg( + Arg::with_name("config-dir") + .long("config-dir") + .short("d") + .help("Show bat's configuration directory."), + ) + .group( + ArgGroup::with_name("cache-actions") + .args(&["init", "clear", "config-dir"]) + .required(arg_group_required), + ) + .arg( + Arg::with_name("source") + .long("source") + .requires("init") + .takes_value(true) + .value_name("dir") + .help("Use a different directory to load syntaxes and themes from."), + ) + .arg( + Arg::with_name("target") + .long("target") + .requires("init") + .takes_value(true) + .value_name("dir") + .help( + "Use a different directory to store the cached syntax and theme set.", + ), + ) + .arg(Arg::with_name("blank").long("blank").requires("init").help( + "Create completely new syntax and theme sets \ + (instead of appending to the default sets).", + )), + ) + .help_message("Print this help message.") + .version_message("Show version information.") +} diff --git a/src/main.rs b/src/main.rs index bc3d11aa..751212f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ extern crate syntect; mod app; mod assets; +mod clap_app; mod controller; mod decorations; mod diff;