Skip to main content

wt/util/
editor.rs

1//! Editor resolution (spec §10/§11): the `editor` config key, then `$VISUAL`,
2//! then `$EDITOR`.
3
4use crate::cx::Env;
5use crate::error::{Error, Result};
6
7/// Resolves the editor command, erroring if none is configured.
8pub fn resolve_editor(config_editor: Option<&str>, env: &Env) -> Result<String> {
9    if let Some(editor) = config_editor.filter(|e| !e.is_empty()) {
10        return Ok(editor.to_string());
11    }
12    if let Some(visual) = env.get("VISUAL").filter(|v| !v.is_empty()) {
13        return Ok(visual.to_string());
14    }
15    if let Some(editor) = env.get("EDITOR").filter(|e| !e.is_empty()) {
16        return Ok(editor.to_string());
17    }
18    Err(Error::operation(
19        "no editor configured; set the `editor` config key, $VISUAL, or $EDITOR",
20    ))
21}
22
23/// Splits an editor command into argv (honoring quotes).
24pub fn editor_argv(command: &str) -> Vec<String> {
25    shell_words::split(command).unwrap_or_else(|_| vec![command.to_string()])
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use std::collections::HashMap;
32
33    fn env(pairs: &[(&str, &str)]) -> Env {
34        Env::from_map(
35            pairs
36                .iter()
37                .map(|(k, v)| ((*k).to_string(), (*v).to_string()))
38                .collect::<HashMap<_, _>>(),
39        )
40    }
41
42    #[test]
43    fn config_editor_wins() {
44        let e = env(&[("VISUAL", "vim"), ("EDITOR", "nano")]);
45        assert_eq!(resolve_editor(Some("hx"), &e).unwrap(), "hx");
46    }
47
48    #[test]
49    fn falls_back_to_visual_then_editor() {
50        let e = env(&[("VISUAL", "vim"), ("EDITOR", "nano")]);
51        assert_eq!(resolve_editor(None, &e).unwrap(), "vim");
52        let e2 = env(&[("EDITOR", "nano")]);
53        assert_eq!(resolve_editor(None, &e2).unwrap(), "nano");
54    }
55
56    #[test]
57    fn empty_values_are_skipped() {
58        let e = env(&[("VISUAL", ""), ("EDITOR", "nano")]);
59        assert_eq!(resolve_editor(Some(""), &e).unwrap(), "nano");
60    }
61
62    #[test]
63    fn errors_when_unset() {
64        assert!(resolve_editor(None, &env(&[])).is_err());
65    }
66
67    #[test]
68    fn argv_splits_quoted_command() {
69        assert_eq!(editor_argv("code --wait"), vec!["code", "--wait"]);
70        assert_eq!(editor_argv("\"my editor\" -n"), vec!["my editor", "-n"]);
71    }
72}