From 5bc2b255aceb40e001620e7f2035ecff44f9b1b7 Mon Sep 17 00:00:00 2001 From: Omnikar Date: Fri, 18 Mar 2022 13:39:44 -0400 Subject: [PATCH 1/2] Make `:write` create nonexistent subdirectories Prompting as to whether this should take place remains a TODO. --- helix-view/src/document.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 671ceb75f84f..dab2f1ff8ac7 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -471,7 +471,7 @@ impl Document { if let Some(parent) = path.parent() { // TODO: display a prompt asking the user if the directories should be created if !parent.exists() { - bail!("can't save file, parent directory does not exist"); + std::fs::DirBuilder::new().recursive(true).create(parent)?; } } From 7927781dfe2770664267a4f53caf042e27d97f35 Mon Sep 17 00:00:00 2001 From: Omnikar Date: Tue, 5 Apr 2022 20:09:09 -0400 Subject: [PATCH 2/2] Move subdirectory creation to new `w!` command --- book/src/generated/typable-cmd.md | 1 + helix-term/src/commands/typed.rs | 31 +++++++++++++++++++++++++------ helix-view/src/document.rs | 14 ++++++++++---- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 2d89c97f9dfa..197c91c6d88d 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -10,6 +10,7 @@ | `:buffer-close-all`, `:bca`, `:bcloseall` | Close all buffers, without quiting. | | `:buffer-close-all!`, `:bca!`, `:bcloseall!` | Close all buffers forcefully (ignoring unsaved changes), without quiting. | | `:write`, `:w` | Write changes to disk. Accepts an optional path (:write some/path.txt) | +| `:write!`, `:w!` | Write changes to disk forcefully (creating necessary subdirectories). Accepts an optional path (:write some/path.txt) | | `:new`, `:n` | Create a new scratch buffer. | | `:format`, `:fmt` | Format the file using the LSP formatter. | | `:indent-style` | Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.) | diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index d35b7082fda4..54c85a66c141 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -172,7 +172,11 @@ fn force_buffer_close_all( buffer_close_by_ids_impl(cx.editor, &document_ids, true) } -fn write_impl(cx: &mut compositor::Context, path: Option<&Cow>) -> anyhow::Result<()> { +fn write_impl( + cx: &mut compositor::Context, + path: Option<&Cow>, + force: bool, +) -> anyhow::Result<()> { let jobs = &mut cx.jobs; let doc = doc_mut!(cx.editor); @@ -194,7 +198,7 @@ fn write_impl(cx: &mut compositor::Context, path: Option<&Cow>) -> anyhow:: jobs.callback(callback); shared }); - let future = doc.format_and_save(fmt); + let future = doc.format_and_save(fmt, force); cx.jobs.add(Job::new(future).wait_before_exiting()); if path.is_some() { @@ -209,7 +213,15 @@ fn write( args: &[Cow], _event: PromptEvent, ) -> anyhow::Result<()> { - write_impl(cx, args.first()) + write_impl(cx, args.first(), false) +} + +fn force_write( + cx: &mut compositor::Context, + args: &[Cow], + _event: PromptEvent, +) -> anyhow::Result<()> { + write_impl(cx, args.first(), true) } fn new_file( @@ -362,7 +374,7 @@ fn write_quit( args: &[Cow], event: PromptEvent, ) -> anyhow::Result<()> { - write_impl(cx, args.first())?; + write_impl(cx, args.first(), false)?; quit(cx, &[], event) } @@ -371,7 +383,7 @@ fn force_write_quit( args: &[Cow], event: PromptEvent, ) -> anyhow::Result<()> { - write_impl(cx, args.first())?; + write_impl(cx, args.first(), true)?; force_quit(cx, &[], event) } @@ -428,7 +440,7 @@ fn write_all_impl( jobs.callback(callback); shared }); - let future = doc.format_and_save(fmt); + let future = doc.format_and_save(fmt, force); jobs.add(Job::new(future).wait_before_exiting()); } @@ -1066,6 +1078,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: write, completer: Some(completers::filename), }, + TypableCommand { + name: "write!", + aliases: &["w!"], + doc: "Write changes to disk forcefully (creating necessary subdirectories). Accepts an optional path (:write some/path.txt)", + fun: force_write, + completer: Some(completers::filename), + }, TypableCommand { name: "new", aliases: &["n"], diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index dab2f1ff8ac7..2ef8136c94c5 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -430,15 +430,16 @@ impl Document { Some(fut) } - pub fn save(&mut self) -> impl Future> { - self.save_impl::>(None) + pub fn save(&mut self, force: bool) -> impl Future> { + self.save_impl::>(None, force) } pub fn format_and_save( &mut self, formatting: Option>, + force: bool, ) -> impl Future> { - self.save_impl(formatting) + self.save_impl(formatting, force) } // TODO: do we need some way of ensuring two save operations on the same doc can't run at once? @@ -450,6 +451,7 @@ impl Document { fn save_impl>( &mut self, formatting: Option, + force: bool, ) -> impl Future> { // we clone and move text + path into the future so that we asynchronously save the current // state without blocking any further edits. @@ -471,7 +473,11 @@ impl Document { if let Some(parent) = path.parent() { // TODO: display a prompt asking the user if the directories should be created if !parent.exists() { - std::fs::DirBuilder::new().recursive(true).create(parent)?; + if force { + std::fs::DirBuilder::new().recursive(true).create(parent)?; + } else { + bail!("can't save file, parent directory does not exist"); + } } }