Skip to content
Advertisement

How do I use Rust to open the user’s default editor and get the edited content?

When you use git commit without the -m flag it opens your default editor, which is set by the $EDITOR environment variable. Doing so enables you to edit multiple lines, navigate with the arrows and submit the text when you’re done. I’d like to do something similar with my command line program. It could be opening the default editor or something that has the features I just mentioned above.

I tried using the open crate, but it opens the $VISUAL editor and doesn’t seem to have a parameter to specify which editor to open.

I tried using io::stdin() with read_to_end(). It permits multiple lines entry and using Ctrl-D to submit text, but not using the arrows to move and edit the text.

I tried using the rustyline crate, but I can’t find how to get multiple lines while being able to edit previous lines.

Advertisement

Answer

I could be wrong, but I think the way git commit works is that it creates a temporary file (.git/COMMIT_EDITMSG) and then as a sub-process it opens that file in the user’s $EDITOR and then waits for the editor’s process to exit/return. That will only happen when the user closes their editor. Which basically means that they can use the full power of their choice of editors, i.e. navigate in the file, save their changes, etc.

Thus, in Rust, you could use std::process::Command to open the user’s editor, which you can get via the std::env::var. You can store the temporary file in a specific location if your application has one (like the .git directory for git or ~/.config/<your-app>/<your-file>, etc.) or you could create a temporary one inside the system’s temporary directory returned to you by the std::env::temp_dir. (Alternatively you could use the excellent third party crate to directly create a temporary file only: tempfile)

Here’s a working example using the above mentioned technique:

use std::{
    env::{temp_dir, var},
    fs::File,
    io::Read,
    process::Command,
};

fn main() {
    let editor = var("EDITOR").unwrap();
    let mut file_path = temp_dir();
    file_path.push("editable");
    File::create(&file_path).expect("Could not create file");

    Command::new(editor)
        .arg(&file_path)
        .status()
        .expect("Something went wrong");

    let mut editable = String::new();
    File::open(file_path)
        .expect("Could not open file")
        .read_to_string(&mut editable);

    println!("File content:n{}", editable);
}
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement