diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index e1bb8ee32..d6ba238e0 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -663,6 +663,28 @@ fn force_write_quit( force_quit(cx, Args::default(), event) } +/// exit command: Write only if named and modified (but not externally), then quit. +fn write_exit(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let (_, doc) = current!(cx.editor); + + // Current document named and modified? + // Write changes if not externally modified already. + if doc.is_modified() { + if doc.path().is_some() && !doc.externally_overwritten() { + write_impl(cx, None, false)?; + cx.block_try_flush_writes()?; + } else { + doc.reset_modified(); + } + } + + quit(cx, Args::default(), event) +} + /// Results in an error if there are modified buffers remaining and sets editor /// error, otherwise returns `Ok(())`. If the current document is unmodified, /// and there are modified documents, switches focus to one of them. @@ -2816,7 +2838,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ }, TypableCommand { name: "write-quit", - aliases: &["wq", "x"], + aliases: &["wq"], doc: "Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt)", fun: write_quit, completer: CommandCompleter::positional(&[completers::filename]), @@ -2827,7 +2849,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ }, TypableCommand { name: "write-quit!", - aliases: &["wq!", "x!"], + aliases: &["wq!"], doc: "Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt)", fun: force_write_quit, completer: CommandCompleter::positional(&[completers::filename]), @@ -2836,6 +2858,17 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ ..Signature::DEFAULT }, }, + TypableCommand { + name: "exit", + aliases: &["xit", "x", "x!"], + doc: "Save the current view if named and modified (but not externally), then quit.", + fun: write_exit, + completer: CommandCompleter::none(), + signature: Signature { + positionals: (0, Some(0)), + ..Signature::DEFAULT + }, + }, TypableCommand { name: "write-all", aliases: &["wa"], diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index a0a56113c..874f84d6e 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1199,6 +1199,13 @@ impl Document { }; } + /// Path given and externally modified since `last_saved_time`? + pub fn externally_overwritten(&self) -> bool { + self.path() + .and_then(|p| p.metadata().ok().and_then(|m| m.modified().ok())) + .is_some_and(|mtime| mtime > self.last_saved_time) + } + // Detect if the file is readonly and change the readonly field if necessary (unix only) pub fn detect_readonly(&mut self) { // Allows setting the flag for files the user cannot modify, like root files