mod app;
mod host;
mod render;
mod syntax;
use anyhow::Result;
use crossterm::{execute, terminal};
use ratatui::{Terminal, backend::CrosstermBackend};
use std::io::{self, stdout};
pub struct Args {
pub file: Option<std::path::PathBuf>,
pub line: Option<usize>,
pub pattern: Option<String>,
pub readonly: bool,
}
fn parse_args() -> Result<Args> {
let raw: Vec<String> = std::env::args().collect();
let mut line: Option<usize> = None;
let mut pattern: Option<String> = None;
let mut readonly = false;
let mut file: Option<std::path::PathBuf> = None;
let mut i = 1usize;
while i < raw.len() {
let arg = &raw[i];
if arg == "-R" || arg == "--readonly" {
readonly = true;
} else if arg == "--version" || arg == "-V" {
println!("hjkl {}", env!("CARGO_PKG_VERSION"));
std::process::exit(0);
} else if arg == "--help" || arg == "-h" {
print_help();
std::process::exit(0);
} else if let Some(rest) = arg.strip_prefix('+') {
if let Some(pat) = rest.strip_prefix('/') {
pattern = Some(pat.to_string());
} else if let Ok(n) = rest.parse::<usize>() {
line = Some(n);
} else {
eprintln!("hjkl: ignoring unknown +cmd: {arg}");
}
} else if arg.starts_with('-') {
eprintln!("hjkl: unknown flag: {arg}");
std::process::exit(1);
} else {
file = Some(std::path::PathBuf::from(arg));
}
i += 1;
}
Ok(Args {
file,
line,
pattern,
readonly,
})
}
fn print_help() {
println!(
"hjkl {} — vim-modal terminal editor\n\nUSAGE:\n hjkl [OPTIONS] [FILE]\n\nOPTIONS:\n -R, --readonly Open file read-only\n +N Jump to line N on open\n +/PATTERN Search for PATTERN on open\n -h, --help Show this help\n -V, --version Print version",
env!("CARGO_PKG_VERSION")
);
}
fn main() -> Result<()> {
let args = parse_args()?;
let mut app = app::App::new(args.file, args.readonly, args.line, args.pattern)?;
terminal::enable_raw_mode()?;
execute!(stdout(), terminal::EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout());
let mut terminal = Terminal::new(backend)?;
let result = app.run(&mut terminal);
let _ = terminal::disable_raw_mode();
let _ = execute!(io::stdout(), terminal::LeaveAlternateScreen);
result
}