use anyhow::{Context, Result};
use std::io::Write;
use std::process::Command;
use crate::config::SivtrConfig;
const FALLBACK_EDITORS: &[&str] = &["hx", "nvim", "vim", "vi", "nano", "notepad"];
pub fn resolve_editor() -> Result<String> {
resolve_editor_with_config(&SivtrConfig::load().unwrap_or_default())
}
pub fn resolve_editor_with_config(config: &SivtrConfig) -> Result<String> {
if !config.editor.command.is_empty() {
return Ok(config.editor.command.clone());
}
for &name in FALLBACK_EDITORS {
if which_exists(name) {
return Ok(name.to_string());
}
}
anyhow::bail!(
"No editor found. Set editor.command in config file \
(run `sivtr config init` to create one)"
)
}
pub fn open_in_editor(content: &str) -> Result<String> {
let editor = resolve_editor()?;
let mut tmp = tempfile::Builder::new()
.prefix("sivtr-")
.suffix(".txt")
.tempfile()
.context("Failed to create temp file")?;
tmp.write_all(content.as_bytes())
.context("Failed to write to temp file")?;
tmp.flush()?;
let path = tmp.path().to_path_buf();
let parts: Vec<&str> = editor.split_whitespace().collect();
let (program, extra_args) = parts.split_first().context("Empty editor command")?;
let status = Command::new(program)
.args(extra_args)
.arg(&path)
.status()
.with_context(|| format!("Failed to launch editor '{editor}'"))?;
if !status.success() {
anyhow::bail!("Editor '{editor}' exited with {status}");
}
let result = std::fs::read_to_string(&path).context("Failed to read back from temp file")?;
Ok(result)
}
fn which_exists(name: &str) -> bool {
#[cfg(windows)]
{
Command::new("where")
.arg(name)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
#[cfg(not(windows))]
{
Command::new("which")
.arg(name)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
}