Smart Neovim launcher for yyx990803/launch-editor
1use std::{num::NonZeroU64, path::Path, process::ExitCode};
2
3use anyhow::Result;
4use log::{debug, error, info, warn};
5
6mod args;
7mod nvim;
8mod platform;
9
10fn main() -> ExitCode {
11 env_logger::builder()
12 .format_timestamp(None)
13 .filter_level(log::LevelFilter::Info)
14 .parse_default_env()
15 .init();
16
17 let Ok(args) = args::parse() else {
18 let mut args = std::env::args();
19 let program_name = args.next();
20 let program_name = program_name.as_deref().unwrap_or("launch-editor-nvim");
21
22 warn!("Usage: {program_name} <filename> [line] [column]");
23 return ExitCode::FAILURE;
24 };
25
26 match edit_with_nvim(&args.filename, args.line, args.col) {
27 Ok(code) => code,
28 Err(e) => {
29 error!("{e}");
30 ExitCode::FAILURE
31 }
32 }
33}
34
35fn edit_with_nvim(
36 path: &Path,
37 line: Option<NonZeroU64>,
38 col: Option<NonZeroU64>,
39) -> Result<ExitCode> {
40 if log::log_enabled!(log::Level::Info) {
41 let path = path.display();
42 match (line, col) {
43 (Some(line), Some(col)) => info!("Opening {path}:{line}:{col}"),
44 (Some(line), _) => info!("Opening {path}:{line}"),
45 _ => info!("Opening {path}"),
46 }
47 }
48
49 let path = path.canonicalize()?;
50 let best_nvim = nvim::instances()?
51 .filter_map(|nvim| {
52 let pid = nvim.process.pid();
53 let cwd = nvim.process.current_dir();
54
55 debug!("Neovim {pid}: cwd={}", cwd.display());
56 path.strip_prefix(cwd)
57 .inspect_err(|_| debug!("Neovim {pid} rejected: file outside cwd"))
58 .ok()
59 .zip(Some(nvim))
60 })
61 .min_by_key(|(suffix, nvim)| {
62 let n = suffix.components().count();
63 debug!(
64 "Neovim {pid} candidate: score = {n} (lower is better)",
65 pid = nvim.process.pid()
66 );
67 n
68 })
69 .map(|(_, nvim)| nvim);
70
71 match best_nvim {
72 Some(nvim) => {
73 nvim.edit(&path, line, col)?;
74 Ok(ExitCode::SUCCESS)
75 }
76 None => {
77 warn!("No suitable Neovim instance was found!");
78 info!("tip: the path must be within Neovim's current working directory");
79 info!("tip: try again with RUST_LOG=debug");
80 Ok(ExitCode::FAILURE)
81 }
82 }
83}