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