mod app;
mod editorconfig;
mod git;
mod host;
mod picker;
mod picker_sources;
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 files: Vec<std::path::PathBuf>,
pub line: Option<usize>,
pub pattern: Option<String>,
pub readonly: bool,
pub perf: bool,
pub picker: 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 perf = false;
let mut picker = false;
let mut files: Vec<std::path::PathBuf> = Vec::new();
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 if rest == "perf" {
perf = true;
} else if rest == "picker" {
picker = true;
} else {
eprintln!("hjkl: ignoring unknown +cmd: {arg}");
}
} else if arg.starts_with('-') {
eprintln!("hjkl: unknown flag: {arg}");
std::process::exit(1);
} else {
files.push(std::path::PathBuf::from(arg));
}
i += 1;
}
Ok(Args {
files,
line,
pattern,
readonly,
perf,
picker,
})
}
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 +perf Enable :perf overlay at startup\n +picker Open the file picker at startup\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.files.first().cloned(),
args.readonly,
args.line,
args.pattern,
)?;
for path in args.files.into_iter().skip(1) {
if let Err(e) = app.open_extra(path) {
eprintln!("hjkl: {e}");
}
}
if args.perf {
app.perf_overlay = true;
}
if args.picker {
app.open_picker();
}
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
}