use std::{env, path::PathBuf}; use vamp::{session::EditorSession, tui}; const HELP: &str = "vamp — modal editor with isolated Ruby plugins Usage: vamp [--config PATH | --no-config] [FILE] Normal: i/a insert, I/A insert at first nonblank/end, o/O new line h/j/k/l move, 0/$ line ends, x delete, J join lines u undo, U redo, : command Insert: Esc returns to normal Commands: :w [PATH], :q, :q!, :wq [PATH], :plugins, :PLUGIN [ARGS] Ruby config: $XDG_CONFIG_HOME/vamp/config.rb or ~/.config/vamp/config.rb Set VAMP_RUBY to choose the Ruby executable."; struct Options { file: Option, config: Option, } fn options() -> Result, String> { let mut args = env::args_os().skip(1); let mut file = None; let mut config = None; let mut config_selected = false; let mut positional = false; while let Some(arg) = args.next() { if !positional && (arg == "--help" || arg == "-h") { println!("{HELP}"); return Ok(None); } else if !positional && arg == "--" { positional = true; } else if !positional && arg == "--config" { if config_selected { return Err("use only one of --config and --no-config".into()); } config = Some(PathBuf::from( args.next().ok_or("--config requires a path")?, )); config_selected = true; } else if !positional && arg == "--no-config" { if config_selected { return Err("use only one of --config and --no-config".into()); } config_selected = true; } else if !positional && arg.to_string_lossy().starts_with('-') { return Err(format!("unknown option: {}", arg.to_string_lossy())); } else if file.replace(PathBuf::from(arg)).is_some() { return Err("only one file can be opened".into()); } } if !config_selected { let home = env::var_os("XDG_CONFIG_HOME") .map(PathBuf::from) .or_else(|| env::var_os("HOME").map(|home| PathBuf::from(home).join(".config"))); config = home .map(|home| home.join("vamp/config.rb")) .filter(|path| path.exists()); } Ok(Some(Options { file, config })) } fn run() -> Result<(), String> { let Some(options) = options()? else { return Ok(()); }; let mut session = EditorSession::open( options.file, tui::CAPABILITIES.iter().map(|s| (*s).to_owned()).collect(), options.config.as_deref(), ) .map_err(|error| format!("cannot open file: {error}"))?; tui::run(&mut session).map_err(|error| format!("terminal error: {error}")) } fn main() { if let Err(error) = run() { eprintln!("vamp: {error}"); std::process::exit(1); } }