1#![feature(exit_status_error)]
6
7use core::str::FromStr;
8
9use rootcause::prelude::ResultExt as _;
10use rootcause::report;
11use ytil_editor::Editor;
12use ytil_editor::FileToOpen;
13use ytil_sys::cli::Args;
14
15struct Env {
17 name: &'static str,
19 value: String,
21}
22
23impl Env {
24 pub fn by_ref(&self) -> (&'static str, &str) {
26 (self.name, &self.value)
27 }
28}
29
30fn get_enriched_path_env() -> rootcause::Result<Env> {
35 let enriched_path = [
36 &std::env::var("PATH").unwrap_or_else(|_| String::new()),
37 "/opt/homebrew/bin",
38 &ytil_sys::dir::build_home_path(&[".local", "bin"])?.to_string_lossy(),
39 ]
40 .join(":");
41
42 Ok(Env {
43 name: "PATH",
44 value: enriched_path,
45 })
46}
47
48fn escape_single_quotes(s: &str) -> String {
52 s.replace('\'', "'\\''")
53}
54
55#[ytil_sys::main]
57fn main() -> rootcause::Result<()> {
58 let enriched_path_env = get_enriched_path_env()?;
59 let args = ytil_sys::cli::get();
60
61 if args.has_help() {
62 println!("{}", include_str!("../help.txt"));
63 return Ok(());
64 }
65
66 let Some(editor) = args.first().map(|x| Editor::from_str(x)).transpose()? else {
67 return Err(report!("missing editor arg")).attach_with(|| format!("args={args:#?}"));
68 };
69
70 let Some(file_to_open) = args.get(1) else {
71 return Err(report!("missing file arg")).attach_with(|| format!("args={args:#?}"));
72 };
73
74 let pane_id = match args.get(2) {
75 Some(x) => x.parse()?,
76 None => ytil_wezterm::get_current_pane_id()?,
77 };
78
79 let panes = ytil_wezterm::get_all_panes(&[enriched_path_env.by_ref()])?;
80
81 let file_to_open = FileToOpen::try_from((file_to_open.as_str(), pane_id, panes.as_slice()))?;
82
83 let editor_pane_id =
84 ytil_wezterm::get_sibling_pane_with_titles(&panes, pane_id, editor.pane_titles()).map(|x| x.pane_id)?;
85
86 let open_file_cmd = editor.open_file_cmd(&file_to_open);
87 let escaped_open_file_cmd = escape_single_quotes(&open_file_cmd);
88
89 ytil_cmd::silent_cmd("sh")
90 .args([
91 "-c",
92 &format!(
93 "{} && {} && {} && {}",
94 ytil_wezterm::send_text_to_pane_cmd(r"$'\e'", editor_pane_id),
97 ytil_wezterm::send_text_to_pane_cmd(&format!("'{escaped_open_file_cmd}'"), editor_pane_id),
98 ytil_wezterm::submit_pane_cmd(editor_pane_id),
99 ytil_wezterm::activate_pane_cmd(editor_pane_id),
100 ),
101 ])
102 .envs(std::iter::once(enriched_path_env.by_ref()))
103 .spawn()?;
104
105 Ok(())
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[rstest::rstest]
113 #[case::no_quotes("hello world", "hello world")]
114 #[case::single_quote("it's here", "it'\\''s here")]
115 #[case::multiple_quotes("a'b'c", "a'\\''b'\\''c")]
116 #[case::only_quote("'", "'\\''")]
117 #[case::empty("", "")]
118 fn escape_single_quotes_produces_expected_output(#[case] input: &str, #[case] expected: &str) {
119 pretty_assertions::assert_eq!(escape_single_quotes(input), expected);
120 }
121}