delta_lib/
mainfn.rs

1use std::io::{self, ErrorKind};
2use std::process;
3
4use bytelines::ByteLinesReader;
5
6use crate::cli;
7use crate::config;
8use crate::delta::delta;
9use crate::fatal;
10use crate::git_config;
11use crate::subcommands;
12use crate::utils;
13use crate::utils::bat::assets::list_languages;
14use crate::utils::bat::output::OutputType;
15
16#[cfg(not(tarpaulin_include))]
17/// Run `delta`. This parses the commandline arguments, reads configs and
18/// displays the diff. Then it will exit the process ⚠.
19pub fn mainfn() -> std::io::Result<()> {
20    // Do this first because both parsing all the input in `run_app()` and
21    // listing all processes takes about 50ms on Linux.
22    // It also improves the chance that the calling process is still around when
23    // input is piped into delta (e.g. `git show  --word-diff=color | delta`).
24    utils::process::start_determining_calling_process_in_thread();
25
26    // Ignore ctrl-c (SIGINT) to avoid leaving an orphaned pager process.
27    // See https://github.com/dandavison/delta/issues/681
28    ctrlc::set_handler(|| {})
29        .unwrap_or_else(|err| eprintln!("Failed to set ctrl-c handler: {}", err));
30    let exit_code = run_app()?;
31    // when you call process::exit, no destructors are called, so we want to do it only once, here
32    process::exit(exit_code);
33}
34
35#[cfg(not(tarpaulin_include))]
36// An Ok result contains the desired process exit code. Note that 1 is used to
37// report that two files differ when delta is called with two positional
38// arguments and without standard input; 2 is used to report a real problem.
39fn run_app() -> std::io::Result<i32> {
40    let assets = utils::bat::assets::load_highlighting_assets();
41    let opt = cli::Opt::from_args_and_git_config(git_config::GitConfig::try_create(), assets);
42
43    let subcommand_result = if opt.list_languages {
44        Some(list_languages())
45    } else if opt.list_syntax_themes {
46        Some(subcommands::list_syntax_themes::list_syntax_themes())
47    } else if opt.show_syntax_themes {
48        Some(subcommands::show_syntax_themes::show_syntax_themes())
49    } else if opt.show_themes {
50        Some(subcommands::show_themes::show_themes(
51            opt.dark,
52            opt.light,
53            opt.computed.is_light_mode,
54        ))
55    } else if opt.show_colors {
56        Some(subcommands::show_colors::show_colors())
57    } else if opt.parse_ansi {
58        Some(subcommands::parse_ansi::parse_ansi())
59    } else {
60        None
61    };
62    if let Some(result) = subcommand_result {
63        if let Err(error) = result {
64            match error.kind() {
65                ErrorKind::BrokenPipe => {}
66                _ => fatal(format!("{}", error)),
67            }
68        }
69        return Ok(0);
70    };
71
72    let _show_config = opt.show_config;
73    let config = config::Config::from(opt);
74
75    if _show_config {
76        let stdout = io::stdout();
77        let mut stdout = stdout.lock();
78        subcommands::show_config::show_config(&config, &mut stdout)?;
79        return Ok(0);
80    }
81
82    let mut output_type =
83        OutputType::from_mode(config.paging_mode, config.pager.clone(), &config).unwrap();
84    let mut writer = output_type.handle().unwrap();
85
86    match (config.minus_file.as_ref(), config.plus_file.as_ref()) {
87        (None, None) => {}
88        (Some(minus_file), Some(plus_file)) => {
89            let exit_code = subcommands::diff::diff(minus_file, plus_file, &config, &mut writer);
90            return Ok(exit_code);
91        }
92        _ => {
93            eprintln!(
94                "\
95    The main way to use delta is to configure it as the pager for git: \
96    see https://github.com/dandavison/delta#configuration. \
97    You can also use delta to diff two files: `delta file_A file_B`."
98            );
99            return Ok(config.error_exit_code);
100        }
101    }
102
103    if let Err(error) = delta(io::stdin().lock().byte_lines(), &mut writer, &config) {
104        match error.kind() {
105            ErrorKind::BrokenPipe => return Ok(0),
106            _ => eprintln!("{}", error),
107        }
108    };
109    Ok(0)
110}