//! CLI option parsing.
use std::{
env,
ffi::OsStr,
fmt::Display,
path::{Path, PathBuf},
};
use clap::{crate_authors, crate_description, Arg, Command};
use const_format::formatcp;
use crossterm::tty::IsTty;
use itertools::Itertools;
use crate::{
display::style::BackgroundColor,
exit_codes::EXIT_BAD_ARGUMENTS,
parse::guess_language::{language_override_from_name, LanguageOverride},
version::VERSION,
};
pub(crate) const DEFAULT_BYTE_LIMIT: usize = 1_000_000;
// Chosen experimentally: this is sufficiently many for all the sample
// files (the highest is slow_1.rs/slow_2.rs at 1.3M nodes), but
// small enough to terminate in ~5 seconds like the test file in #306.
pub(crate) const DEFAULT_GRAPH_LIMIT: usize = 3_000_000;
pub(crate) const DEFAULT_PARSE_ERROR_LIMIT: usize = 0;
pub(crate) const DEFAULT_TAB_WIDTH: usize = 4;
pub(crate) const USAGE: &str = concat!(env!("CARGO_BIN_NAME"), " [OPTIONS] OLD-PATH NEW-PATH");
#[derive(Debug, Clone, Copy)]
pub(crate) enum ColorOutput {
Always,
Auto,
Never,
}
#[derive(Debug, Clone)]
pub(crate) struct DisplayOptions {
pub(crate) background_color: BackgroundColor,
pub(crate) use_color: bool,
pub(crate) display_mode: DisplayMode,
pub(crate) print_unchanged: bool,
pub(crate) tab_width: usize,
pub(crate) terminal_width: usize,
pub(crate) num_context_lines: u32,
pub(crate) syntax_highlight: bool,
pub(crate) sort_paths: bool,
}
pub(crate) const DEFAULT_TERMINAL_WIDTH: usize = 80;
impl Default for DisplayOptions {
fn default() -> Self {
Self {
background_color: BackgroundColor::Dark,
use_color: false,
display_mode: DisplayMode::SideBySide,
print_unchanged: true,
tab_width: 8,
terminal_width: DEFAULT_TERMINAL_WIDTH,
num_context_lines: 3,
syntax_highlight: true,
sort_paths: false,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct DiffOptions {
pub(crate) graph_limit: usize,
pub(crate) byte_limit: usize,
pub(crate) parse_error_limit: usize,
pub(crate) check_only: bool,
pub(crate) ignore_comments: bool,
pub(crate) strip_cr: bool,
}
impl Default for DiffOptions {
fn default() -> Self {
Self {
graph_limit: DEFAULT_GRAPH_LIMIT,
byte_limit: DEFAULT_BYTE_LIMIT,
parse_error_limit: DEFAULT_PARSE_ERROR_LIMIT,
check_only: false,
ignore_comments: false,
strip_cr: false,
}
}
}
fn app() -> clap::Command<'static> {
Command::new("Difftastic")
.override_usage(USAGE)
.version(env!("CARGO_PKG_VERSION"))
.long_version(VERSION.as_str())
.about(crate_description!())
.author(crate_authors!())
.after_long_help(concat!(
"You can compare two files with difftastic by specifying them as arguments.\n\n",
"$ ",
env!("CARGO_BIN_NAME"),
" old.js new.js\n\n",
"You can also use directories as arguments. Difftastic will walk both directories and compare files with matching names.\n\n",
"$ ",
env!("CARGO_BIN_NAME"),
" old/ new/\n\n",
"If you have a file with conflict markers, you can pass it as a single argument. Difftastic will diff the two conflicting file states.\n\n",
"$ ",
env!("CARGO_BIN_NAME"),
" file_with_conflicts.js\n\n",
"Difftastic can also be invoked with 7 arguments in the format that GIT_EXTERNAL_DIFF expects.\n\n",
"See the full manual at: https://difftastic.wilfred.me.uk/")
)
.arg(
Arg::new("dump-syntax")
.long("dump-syntax")
.takes_value(true)
.value_name("PATH")
.long_help(
"Parse a single file with tree-sitter and display the difftastic syntax tree.",
).help_heading("DEBUG OPTIONS"),
)
.arg(
Arg::new("dump-syntax-dot")
.long("dump-syntax-dot")
.takes_value(true)
.value_name("PATH")
.long_help(
"Parse a single file with tree-sitter and display the difftastic syntax tree, as a DOT graph.",
).help_heading("DEBUG OPTIONS"),
)
.arg(
Arg::new("dump-ts")
.long("dump-ts")
.takes_value(true)
.value_name("PATH")
.long_help(
"Parse a single file with tree-sitter and display the tree-sitter parse tree.",
).help_heading("DEBUG OPTIONS"),
)
.arg(
Arg::new("context")
.long("context")
.takes_value(true)
.value_name("LINES")
.long_help("The number of contextual lines to show around changed lines.")
.default_value("3")