use std::path::PathBuf;
use anyhow::Result;
use hjkl_buffer::Buffer;
use hjkl_engine::{BufferEdit, DefaultHost, Editor, Options};
use hjkl_ex::ExEffect;
pub fn run(files: Vec<PathBuf>, commands: Vec<String>) -> Result<i32> {
if files.is_empty() && commands.is_empty() {
eprintln!("hjkl --headless: no commands or files; exiting");
return Ok(0);
}
let targets: Vec<Option<PathBuf>> = if files.is_empty() {
vec![None]
} else {
files.into_iter().map(Some).collect()
};
let mut exit_code = 0i32;
for maybe_path in targets {
let display_name = maybe_path
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "<scratch>".to_string());
let mut buffer = Buffer::new();
let mut is_new_file = false;
if let Some(ref path) = maybe_path {
match std::fs::read_to_string(path) {
Ok(content) => {
let content = content.strip_suffix('\n').unwrap_or(&content);
BufferEdit::replace_all(&mut buffer, content);
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
is_new_file = true;
}
Err(e) => {
eprintln!("hjkl: {display_name}: {e}");
exit_code = 1;
continue;
}
}
}
let _ = is_new_file;
let host = DefaultHost::new();
let mut editor = Editor::new(buffer, host, Options::default());
let mut current_filename: Option<PathBuf> = maybe_path.clone();
let mut wrote = false;
for cmd in &commands {
let cmd = cmd.strip_prefix(':').unwrap_or(cmd);
let reg = hjkl_ex::default_registry::<hjkl_engine::DefaultHost>();
let effect = hjkl_ex::try_dispatch(®, &mut editor, cmd)
.unwrap_or(ExEffect::Unknown(cmd.to_string()));
match effect {
ExEffect::None => {}
ExEffect::Ok => {}
ExEffect::Info(_) | ExEffect::InfoTitled { .. } => {
}
ExEffect::Substituted { .. } => {
}
ExEffect::Error(msg) => {
eprintln!("hjkl: {display_name}: {msg}");
exit_code = 1;
}
ExEffect::Unknown(name) => {
eprintln!("hjkl: {display_name}: unknown ex command: {name}");
exit_code = 1;
}
ExEffect::Save => {
if let Err(e) = write_buffer(&editor, ¤t_filename, &display_name) {
eprintln!("{e}");
exit_code = 1;
} else {
wrote = true;
}
}
ExEffect::SaveAs(path_str) => {
let new_path = PathBuf::from(&path_str);
if let Err(e) = write_buffer(&editor, &Some(new_path.clone()), &display_name) {
eprintln!("{e}");
exit_code = 1;
} else {
current_filename = Some(new_path);
wrote = true;
}
}
ExEffect::Quit { save, force: _ } => {
if save {
if let Err(e) = write_buffer(&editor, ¤t_filename, &display_name) {
eprintln!("{e}");
exit_code = 1;
} else {
wrote = true;
}
}
break;
}
ExEffect::EditFile { path, .. } => {
match std::fs::read_to_string(&path) {
Ok(content) => {
let content = content.strip_suffix('\n').unwrap_or(&content);
hjkl_engine::BufferEdit::replace_all(editor.buffer_mut(), content);
current_filename = Some(PathBuf::from(&path));
}
Err(e) => {
eprintln!("hjkl: {path}: {e}");
exit_code = 1;
}
}
}
ExEffect::BufferDelete { .. } => {
break;
}
}
}
let _ = wrote; }
Ok(exit_code)
}
fn write_buffer(
editor: &Editor<Buffer, DefaultHost>,
path: &Option<PathBuf>,
display_name: &str,
) -> Result<(), String> {
match path {
None => Err(format!("hjkl: {display_name}: E32: No file name")),
Some(p) => {
let lines = editor.buffer().lines();
let content = if lines.is_empty() {
String::new()
} else {
let mut s = lines.join("\n");
s.push('\n');
s
};
std::fs::write(p, &content).map_err(|e| format!("hjkl: {}: {e}", p.display()))
}
}
}