Skip to main content

declutter/
editor.rs

1use std::path::Path;
2
3/// How to open a file at a line in the user's editor.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct EditorCommand {
6    pub program: String,
7    pub args: Vec<String>,
8    /// The editor runs in this terminal, so the viewer must step aside until it exits.
9    pub in_terminal: bool,
10}
11
12/// Builds the command for `editor` (the value of `$VISUAL` or `$EDITOR`, which may carry
13/// its own arguments, like `code --wait`), or the system opener when none is set.
14pub fn editor_command(editor: Option<&str>, file: &Path, line: usize) -> EditorCommand {
15    let file = file.display().to_string();
16    let mut words = editor.unwrap_or("").split_whitespace().map(str::to_string);
17    let Some(program) = words.next() else {
18        let opener = if cfg!(target_os = "macos") {
19            "open"
20        } else {
21            "xdg-open"
22        };
23        return EditorCommand {
24            program: opener.to_string(),
25            args: vec![file],
26            in_terminal: false,
27        };
28    };
29    let mut args: Vec<String> = words.collect();
30    let name = Path::new(&program)
31        .file_name()
32        .map(|name| name.to_string_lossy().into_owned())
33        .unwrap_or_default();
34
35    let in_terminal = matches!(
36        name.as_str(),
37        "vim" | "nvim" | "vi" | "nano" | "emacs" | "micro" | "hx" | "helix" | "kak"
38    );
39    match name.as_str() {
40        "code" | "code-insiders" | "cursor" | "windsurf" | "codium" => {
41            args.extend(["-g".to_string(), format!("{file}:{line}")])
42        }
43        "subl" | "zed" | "hx" | "helix" => args.push(format!("{file}:{line}")),
44        "vim" | "nvim" | "vi" | "nano" | "emacs" | "micro" | "kak" => {
45            args.extend([format!("+{line}"), file])
46        }
47        "xed" | "idea" | "pycharm" | "webstorm" | "fleet" | "mate" => {
48            args.extend(["--line".to_string(), line.to_string(), file])
49        }
50        _ => args.push(file),
51    }
52    EditorCommand {
53        program,
54        args,
55        in_terminal,
56    }
57}