aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmnikar2022-04-12 07:52:54 +0000
committerGitHub2022-04-12 07:52:54 +0000
commit660e0e44b2b6329c1d0af788d624dd5765c2acc6 (patch)
tree03a5e162612c5c2e3ebf62530baaab2d13e63da8
parentd5c086697896d87f034c1dbe0ae0ccbc71f37d2c (diff)
Add `:write!` to create nonexistent subdirectories (#1839)
* Make `:write` create nonexistent subdirectories Prompting as to whether this should take place remains a TODO. * Move subdirectory creation to new `w!` command
-rw-r--r--book/src/generated/typable-cmd.md1
-rw-r--r--helix-term/src/commands/typed.rs31
-rw-r--r--helix-view/src/document.rs14
3 files changed, 36 insertions, 10 deletions
diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md
index f9261a75..bb5da3bb 100644
--- a/book/src/generated/typable-cmd.md
+++ b/book/src/generated/typable-cmd.md
@@ -12,6 +12,7 @@
| `:buffer-next`, `:bn`, `:bnext` | Go to next buffer. |
| `:buffer-previous`, `:bp`, `:bprev` | Go to previous buffer. |
| `: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 8f74adb6..d158388f 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -190,7 +190,11 @@ fn buffer_previous(
Ok(())
}
-fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
+fn write_impl(
+ cx: &mut compositor::Context,
+ path: Option<&Cow<str>>,
+ force: bool,
+) -> anyhow::Result<()> {
let jobs = &mut cx.jobs;
let doc = doc_mut!(cx.editor);
@@ -212,7 +216,7 @@ fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> 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() {
@@ -228,7 +232,15 @@ fn write(
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
- write_impl(cx, args.first())
+ write_impl(cx, args.first(), false)
+}
+
+fn force_write(
+ cx: &mut compositor::Context,
+ args: &[Cow<str>],
+ _event: PromptEvent,
+) -> anyhow::Result<()> {
+ write_impl(cx, args.first(), true)
}
fn new_file(
@@ -381,7 +393,7 @@ fn write_quit(
args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
- write_impl(cx, args.first())?;
+ write_impl(cx, args.first(), false)?;
quit(cx, &[], event)
}
@@ -390,7 +402,7 @@ fn force_write_quit(
args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
- write_impl(cx, args.first())?;
+ write_impl(cx, args.first(), true)?;
force_quit(cx, &[], event)
}
@@ -447,7 +459,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());
}
@@ -1141,6 +1153,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
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"],
doc: "Create a new scratch buffer.",
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index 5d739af5..f4b4dc35 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -434,15 +434,16 @@ impl Document {
Some(fut)
}
- pub fn save(&mut self) -> impl Future<Output = Result<(), anyhow::Error>> {
- self.save_impl::<futures_util::future::Ready<_>>(None)
+ pub fn save(&mut self, force: bool) -> impl Future<Output = Result<(), anyhow::Error>> {
+ self.save_impl::<futures_util::future::Ready<_>>(None, force)
}
pub fn format_and_save(
&mut self,
formatting: Option<impl Future<Output = LspFormatting>>,
+ force: bool,
) -> impl Future<Output = anyhow::Result<()>> {
- 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?
@@ -454,6 +455,7 @@ impl Document {
fn save_impl<F: Future<Output = LspFormatting>>(
&mut self,
formatting: Option<F>,
+ force: bool,
) -> impl Future<Output = Result<(), anyhow::Error>> {
// we clone and move text + path into the future so that we asynchronously save the current
// state without blocking any further edits.
@@ -475,7 +477,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() {
- bail!("can't save file, parent directory does not exist");
+ if force {
+ std::fs::DirBuilder::new().recursive(true).create(parent)?;
+ } else {
+ bail!("can't save file, parent directory does not exist");
+ }
}
}