Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
term: allow Editor to be reusable
Fintan Halpenny committed 1 year ago
commit 0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0
parent 23f8cf0d8484cc8b605d47eb32528e3f964d63ef
5 files changed +78 -35
modified radicle-cli/src/commands/config.rs
@@ -1,7 +1,6 @@
#![allow(clippy::or_fun_call)]
use std::ffi::OsString;
use std::path::Path;
-
use std::process;
use std::str::FromStr;

use anyhow::anyhow;
@@ -188,18 +187,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
                path.display()
            );
        }
-
        Operation::Edit => {
-
            let Some(cmd) = term::editor::default_editor() else {
-
                anyhow::bail!("no editor configured; please set the `EDITOR` environment variable");
-
            };
-
            process::Command::new(cmd)
-
                .stdout(process::Stdio::inherit())
-
                .stderr(process::Stdio::inherit())
-
                .stdin(process::Stdio::inherit())
-
                .arg(&path)
-
                .spawn()?
-
                .wait()?;
-
        }
+
        Operation::Edit => match term::editor::Editor::new(&path)?.extension("json").edit()? {
+
            Some(_) => {
+
                term::success!("Successfully made changes to the configuration at {path:?}")
+
            }
+
            None => term::info!("No changes were made to the configuration at {path:?}"),
+
        },
    }

    Ok(())
modified radicle-cli/src/commands/patch/review/builder.rs
@@ -853,7 +853,10 @@ impl CommentBuilder {
        for line in hunk.to_unified_string()?.lines() {
            writeln!(&mut input, "> {line}")?;
        }
-
        let output = term::Editor::new().extension("diff").edit(input)?;
+
        let output = term::Editor::comment()
+
            .extension("diff")
+
            .initial(input)?
+
            .edit()?;

        if let Some(output) = output {
            let header = HunkHeader::try_from(hunk)?;
modified radicle-cli/src/terminal/patch.rs
@@ -51,7 +51,10 @@ impl Message {
        let comment = match self {
            Message::Edit => {
                if io::stderr().is_terminal() {
-
                    term::Editor::new().extension("markdown").edit(help)?
+
                    term::Editor::comment()
+
                        .extension("markdown")
+
                        .initial(help)?
+
                        .edit()?
                } else {
                    Some(help.to_owned())
                }
modified radicle-term/src/editor.rs
@@ -13,26 +13,52 @@ pub const PATHS: &[&str] = &["/usr/local/bin", "/usr/bin", "/bin"];
/// Allows for text input in the configured editor.
pub struct Editor {
    path: PathBuf,
+
    truncate: bool,
+
    cleanup: bool,
}

-
impl Drop for Editor {
-
    fn drop(&mut self) {
-
        fs::remove_file(&self.path).ok();
+
impl Default for Editor {
+
    fn default() -> Self {
+
        Self::comment()
    }
}

-
impl Default for Editor {
-
    fn default() -> Self {
-
        Self::new()
+
impl Drop for Editor {
+
    fn drop(&mut self) {
+
        if self.cleanup {
+
            fs::remove_file(&self.path).ok();
+
        }
    }
}

impl Editor {
    /// Create a new editor.
-
    pub fn new() -> Self {
+
    pub fn new(path: impl AsRef<Path>) -> io::Result<Self> {
+
        let path = path.as_ref();
+
        if path.try_exists()? {
+
            let meta = fs::metadata(path)?;
+
            if !meta.is_file() {
+
                return Err(io::Error::new(
+
                    io::ErrorKind::InvalidInput,
+
                    "must be used to edit a file",
+
                ));
+
            }
+
        }
+
        Ok(Self {
+
            path: path.to_path_buf(),
+
            truncate: false,
+
            cleanup: false,
+
        })
+
    }
+

+
    pub fn comment() -> Self {
        let path = env::temp_dir().join(COMMENT_FILE);

-
        Self { path }
+
        Self {
+
            path,
+
            truncate: true,
+
            cleanup: true,
+
        }
    }

    /// Set the file extension.
@@ -43,26 +69,43 @@ impl Editor {
        self
    }

-
    /// Open the editor and return the edited text.
-
    ///
-
    /// If the text hasn't changed from the initial contents of the editor,
-
    /// return `None`.
-
    pub fn edit(&mut self, initial: impl AsRef<[u8]>) -> io::Result<Option<String>> {
-
        let initial = initial.as_ref();
+
    /// Truncate the file to length 0 when opening
+
    pub fn truncate(mut self, truncate: bool) -> Self {
+
        self.truncate = truncate;
+
        self
+
    }
+

+
    /// Clean up the file after the [`Editor`] is dropped.
+
    pub fn cleanup(mut self, cleanup: bool) -> Self {
+
        self.cleanup = cleanup;
+
        self
+
    }
+

+
    /// Initialize the file with the provided `content`, as long as the file
+
    /// does not already contain anything.
+
    pub fn initial(self, content: impl AsRef<[u8]>) -> io::Result<Self> {
+
        let content = content.as_ref();
        let mut file = fs::OpenOptions::new()
            .write(true)
            .create(true)
-
            .truncate(true)
+
            .truncate(self.truncate)
            .open(&self.path)?;

        if file.metadata()?.len() == 0 {
-
            file.write_all(initial)?;
-
            if !initial.ends_with(&[b'\n']) {
+
            file.write_all(content)?;
+
            if !content.ends_with(&[b'\n']) {
                file.write_all(b"\n")?;
            }
            file.flush()?;
        }
+
        Ok(self)
+
    }

+
    /// Open the editor and return the edited text.
+
    ///
+
    /// If the text hasn't changed from the initial contents of the editor,
+
    /// return `None`.
+
    pub fn edit(&mut self) -> io::Result<Option<String>> {
        let Some(cmd) = self::default_editor() else {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
@@ -126,7 +169,7 @@ impl Editor {
}

/// Get the default editor command.
-
pub fn default_editor() -> Option<OsString> {
+
fn default_editor() -> Option<OsString> {
    // First check the standard environment variables.
    if let Ok(visual) = env::var("VISUAL") {
        if !visual.is_empty() {
modified radicle-tools/src/rad-cli-demo.rs
@@ -35,9 +35,10 @@ fn main() -> anyhow::Result<()> {
            radicle_term::pager::page(table)?;
        }
        "editor" => {
-
            let output = terminal::editor::Editor::new()
+
            let output = terminal::editor::Editor::comment()
                .extension("rs")
-
                .edit("// Enter code here.");
+
                .initial("// Enter code here.")?
+
                .edit();

            match output {
                Ok(Some(s)) => {