use std::ffi::CString;
pub trait Opener {
fn handles_editor(&self, editor: &str) -> bool;
fn form_args(&self, file: &str, line: usize) -> Vec<String>;
}
pub struct DefaultOpener;
impl Opener for DefaultOpener {
fn handles_editor(&self, _editor: &str) -> bool {
true
}
fn form_args(&self, file: &str, _line: usize) -> Vec<String> {
vec![file.to_string()]
}
}
pub struct LessOpener;
impl Opener for LessOpener {
fn handles_editor(&self, editor: &str) -> bool {
let name = file_name(editor);
name == "less" || name == "more"
}
fn form_args(&self, file: &str, line: usize) -> Vec<String> {
vec![format!("+{line}"), "-N".to_string(), file.to_string()]
}
}
pub struct ViOpener;
impl Opener for ViOpener {
fn handles_editor(&self, editor: &str) -> bool {
let name = file_name(editor);
name == "vim" || name == "vi" || name == "nvim"
}
fn form_args(&self, file: &str, line: usize) -> Vec<String> {
vec![file.to_string(), format!("+{line}")]
}
}
pub struct EmacsOpener;
impl Opener for EmacsOpener {
fn handles_editor(&self, editor: &str) -> bool {
file_name(editor) == "emacs"
}
fn form_args(&self, file: &str, line: usize) -> Vec<String> {
vec![format!("+{line}"), file.to_string()]
}
}
pub struct CodeOpener;
impl Opener for CodeOpener {
fn handles_editor(&self, editor: &str) -> bool {
file_name(editor) == "code"
}
fn form_args(&self, file: &str, line: usize) -> Vec<String> {
vec!["-g".to_string(), format!("{file}:{line}")]
}
}
pub struct XedOpener;
impl Opener for XedOpener {
fn handles_editor(&self, editor: &str) -> bool {
file_name(editor) == "xed"
}
fn form_args(&self, file: &str, line: usize) -> Vec<String> {
vec!["-l".to_string(), line.to_string(), file.to_string()]
}
}
pub struct SublimeOpener;
impl Opener for SublimeOpener {
fn handles_editor(&self, editor: &str) -> bool {
file_name(editor) == "subl"
}
fn form_args(&self, file: &str, line: usize) -> Vec<String> {
vec![format!("{file}:{line}")]
}
}
pub fn open(opener: &dyn Opener, editor: String, file: &str, line: usize) {
let command = create_c_string(&editor);
let mut args = opener.form_args(file, line);
args.insert(0, editor);
let args: Vec<CString> = args.into_iter().map(|a| create_c_string(&a)).collect();
let mut ptr_args: Vec<*const i8> = args.iter().map(|a| a.as_ptr()).collect();
ptr_args.push(std::ptr::null());
unsafe {
libc::execvp(command.as_ptr(), ptr_args.as_ptr());
}
}
fn create_c_string(string: &str) -> CString {
CString::new(string).unwrap()
}
fn file_name(path: &str) -> &str {
path.rsplit("/").next().unwrap()
}