oe/
main.rs

1//! Open files (optionally at line:col) in existing Nvim / Helix pane.
2//!
3//! # Arguments
4//! - `editor` Target editor (`nvim` | `hx`).
5//! - `file_path` File to open (append :line:col to jump location).
6//! - `pane_id` Optional `WezTerm` pane ID (auto-detected if omitted).
7//!
8//! # Usage
9//! ```bash
10//! oe nvim src/lib.rs # open file in existing nvim pane
11//! oe nvim src/lib.rs:42:5 # jump to line 42 col 5
12//! oe hx   README.md 1234567890 # explicit pane id
13//! ```
14//!
15//! # Flow
16//! 1. Parse editor + file + optional pane id.
17//! 2. Resolve target pane (sibling) and enrich PATH for `Wezterm`.
18//! 3. Construct editor open command & send keystrokes via `Wezterm` CLI.
19//! 4. Activate target pane.
20//!
21//! # Errors
22//! - Missing editor or file argument.
23//! - Pane id parse or discovery fails.
24//! - Sibling pane detection fails.
25//! - File path parsing / validation fails.
26//! - Spawning shell command fails.
27//! - Required environment variable read fails.
28#![feature(exit_status_error)]
29
30use core::str::FromStr;
31
32use color_eyre::eyre::bail;
33use ytil_editor::Editor;
34use ytil_editor::FileToOpen;
35use ytil_sys::cli::Args;
36
37/// Wrapper for environment variables.
38struct Env {
39    /// The name of the environment variable (static string).
40    name: &'static str,
41    /// The value of the environment variable (dynamically constructed string).
42    value: String,
43}
44
45impl Env {
46    /// Returns environment variable as tuple.
47    pub fn by_ref(&self) -> (&'static str, &str) {
48        (self.name, &self.value)
49    }
50}
51
52/// Creates enriched PATH for `WezTerm` integration.
53///
54/// # Errors
55/// - A required environment variable is missing or invalid Unicode.
56fn get_enriched_path_env() -> color_eyre::Result<Env> {
57    let enriched_path = [
58        &std::env::var("PATH").unwrap_or_else(|_| String::new()),
59        "/opt/homebrew/bin",
60        &ytil_sys::dir::build_home_path(&[".local", "bin"])?.to_string_lossy(),
61    ]
62    .join(":");
63
64    Ok(Env {
65        name: "PATH",
66        value: enriched_path,
67    })
68}
69
70/// Open files (optionally at line:col) in existing Nvim / Helix pane.
71fn main() -> color_eyre::Result<()> {
72    color_eyre::install()?;
73
74    let enriched_path_env = get_enriched_path_env()?;
75    let args = ytil_sys::cli::get();
76
77    if args.has_help() {
78        println!("{}", include_str!("../help.txt"));
79        return Ok(());
80    }
81
82    let Some(editor) = args.first().map(|x| Editor::from_str(x)).transpose()? else {
83        bail!("missing editor arg | args={args:#?}");
84    };
85
86    let Some(file_to_open) = args.get(1) else {
87        bail!("missing file arg | args={args:#?}");
88    };
89
90    let pane_id = match args.get(2) {
91        Some(x) => x.parse()?,
92        None => ytil_wezterm::get_current_pane_id()?,
93    };
94
95    let panes = ytil_wezterm::get_all_panes(&[enriched_path_env.by_ref()])?;
96
97    let file_to_open = FileToOpen::try_from((file_to_open.as_str(), pane_id, panes.as_slice()))?;
98
99    let editor_pane_id =
100        ytil_wezterm::get_sibling_pane_with_titles(&panes, pane_id, editor.pane_titles()).map(|x| x.pane_id)?;
101
102    let open_file_cmd = editor.open_file_cmd(&file_to_open);
103
104    ytil_cmd::silent_cmd("sh")
105        .args([
106            "-c",
107            &format!(
108                "{} && {} && {} && {}",
109                // `wezterm cli send-text $'\e'` sends the "ESC" to `WezTerm` to exit from insert mode
110                // https://github.com/wez/wezterm/discussions/3945
111                ytil_wezterm::send_text_to_pane_cmd(r"$'\e'", editor_pane_id),
112                ytil_wezterm::send_text_to_pane_cmd(&format!("'{open_file_cmd}'"), editor_pane_id),
113                ytil_wezterm::submit_pane_cmd(editor_pane_id),
114                ytil_wezterm::activate_pane_cmd(editor_pane_id),
115            ),
116        ])
117        .envs(std::iter::once(enriched_path_env.by_ref()))
118        .spawn()?;
119
120    Ok(())
121}