use std::path::PathBuf;
use anyhow::Result;
use edamame::app::App;
use edamame::cli::{self, Invocation, RunOpts};
use edamame::config::{self, Config, LoadedConfig};
use edamame::terminal::{self, Capabilities, ColorDepth, TerminalSetup};
const EXIT_USAGE: i32 = 2;
fn main() -> Result<()> {
let invocation = Invocation::parse(std::env::args_os().skip(1)).unwrap_or_else(|e| {
eprintln!("edamame: {e}\n\n{}", cli::USAGE);
std::process::exit(EXIT_USAGE);
});
match invocation {
Invocation::Help => {
print!("{}", cli::help_text());
Ok(())
}
Invocation::Version => {
println!("{}", cli::version_line());
Ok(())
}
Invocation::Doctor => cli::run_doctor(),
Invocation::Run { file, opts } => run(file, opts),
}
}
fn run(file_path: Option<PathBuf>, opts: RunOpts) -> Result<()> {
let truecolor_at_load = Capabilities::detect_color_depth_from_env() == ColorDepth::TrueColor;
let loaded = if opts.no_config {
LoadedConfig::default()
} else {
Config::ensure_default_files(truecolor_at_load);
Config::load(truecolor_at_load, true).unwrap_or_else(|e| {
eprintln!("Warning: failed to load config: {e}. Using defaults.");
LoadedConfig::default()
})
};
let LoadedConfig {
mut config,
keybindings,
theme,
warnings: config_warnings,
} = loaded;
if opts.no_config {
config::disable_config_dir();
config.editor.show_welcome = false;
}
config.dev.logging |= opts.log;
let log_guard = if config.dev.logging {
setup_logging()
} else {
None
};
let TerminalSetup {
terminal,
keyboard_enhancement,
} = terminal::setup()?;
let orig_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = terminal::restore();
orig_hook(info);
}));
let capabilities = Capabilities::detect(keyboard_enhancement);
log_capabilities(&capabilities);
if capabilities.mouse {
if let Err(e) = terminal::enable_mouse() {
tracing::warn!(error = %e, "failed to enable mouse capture");
}
}
let mut app = App::new(
config,
keybindings,
theme,
file_path,
capabilities,
config_warnings,
)?;
let run_result = app.run(terminal);
terminal::restore()?;
if opts.log {
let logging_started = log_guard.is_some();
drop(log_guard);
match Config::log_dir().filter(|_| logging_started) {
Some(dir) => eprintln!(
"edamame: debug log written under {} (debug.log.<date>)",
dir.display()
),
None => eprintln!("edamame: --log could not open a log file; no log was written"),
}
}
run_result
}
fn setup_logging() -> Option<tracing_appender::non_blocking::WorkerGuard> {
let log_dir = Config::log_dir()?;
if std::fs::create_dir_all(&log_dir).is_err() {
return None;
}
let file_appender = tracing_appender::rolling::daily(&log_dir, "debug.log");
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("debug"));
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_writer(non_blocking)
.with_ansi(false)
.init();
tracing::info!("edamame starting");
Some(guard)
}
fn log_capabilities(caps: &Capabilities) {
tracing::info!(
color_depth = ?caps.color_depth,
mouse = caps.mouse,
image_protocol = ?caps.image_protocol,
unicode_full = caps.unicode_full,
keyboard_enhancement = caps.keyboard_enhancement,
"terminal capabilities detected"
);
}