Implement bulk buffer closing commands (#1677)

* Implement buffer-close-all

* Implement buffer-close-others

* Refactor all buffer close variants to use shared logic

* Fix clippy lint

* Docgen for new commands

* Shorten error message for attempting to close buffers that don't exist

* Refactor shared buffer methods to pass only editor, not whole compositor

* Switch signature of bulk buffer closing to use slice of DocumentIds

Addresses feedback that accepting an IntoIterator implementor is too
much for an internal. Also possibly saves some moving?
This commit is contained in:
Daniel S Poulin 2022-02-28 20:31:24 -05:00 committed by GitHub
parent 59c691d2db
commit 78d37fd332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 17 deletions

View File

@ -5,6 +5,10 @@
| `:open`, `:o` | Open a file from disk into the current view. | | `:open`, `:o` | Open a file from disk into the current view. |
| `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. | | `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. |
| `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully (ignoring unsaved changes). | | `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully (ignoring unsaved changes). |
| `:buffer-close-others`, `:bco`, `:bcloseother` | Close all buffers but the currently focused one. |
| `:buffer-close-others!`, `:bco!`, `:bcloseother!` | Close all buffers but the currently focused one. |
| `: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. Accepts an optional path (:write some/path.txt) |
| `:new`, `:n` | Create a new scratch buffer. | | `:new`, `:n` | Create a new scratch buffer. |
| `:format`, `:fmt` | Format the file using the LSP formatter. | | `:format`, `:fmt` | Format the file using the LSP formatter. |

View File

@ -2077,18 +2077,27 @@ pub mod cmd {
Ok(()) Ok(())
} }
fn buffer_close_impl( fn buffer_close_by_ids_impl(
editor: &mut Editor, editor: &mut Editor,
args: &[Cow<str>], doc_ids: &[DocumentId],
force: bool, force: bool,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
for &doc_id in doc_ids {
editor.close_document(doc_id, force)?;
}
Ok(())
}
fn buffer_gather_paths_impl(editor: &mut Editor, args: &[Cow<str>]) -> Vec<DocumentId> {
// No arguments implies current document
if args.is_empty() { if args.is_empty() {
let doc_id = view!(editor).doc; let doc_id = view!(editor).doc;
editor.close_document(doc_id, force)?; return vec![doc_id];
return Ok(());
} }
let mut nonexistent_buffers = vec![]; let mut nonexistent_buffers = vec![];
let mut document_ids = vec![];
for arg in args { for arg in args {
let doc_id = editor.documents().find_map(|doc| { let doc_id = editor.documents().find_map(|doc| {
let arg_path = Some(Path::new(arg.as_ref())); let arg_path = Some(Path::new(arg.as_ref()));
@ -2102,21 +2111,19 @@ pub mod cmd {
}); });
match doc_id { match doc_id {
Some(doc_id) => editor.close_document(doc_id, force)?, Some(doc_id) => document_ids.push(doc_id),
None => nonexistent_buffers.push(arg), None => nonexistent_buffers.push(format!("'{}'", arg)),
} }
} }
let nonexistent_buffers: Vec<_> = nonexistent_buffers if !nonexistent_buffers.is_empty() {
.into_iter()
.map(|str| format!("'{}'", str))
.collect();
editor.set_error(format!( editor.set_error(format!(
"couldn't close buffer(s) {}: does not exist", "cannot close non-existent buffers: {}",
nonexistent_buffers.join(", ") nonexistent_buffers.join(", ")
)); ));
}
Ok(()) document_ids
} }
fn buffer_close( fn buffer_close(
@ -2124,7 +2131,8 @@ pub mod cmd {
args: &[Cow<str>], args: &[Cow<str>],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
buffer_close_impl(cx.editor, args, false) let document_ids = buffer_gather_paths_impl(cx.editor, args);
buffer_close_by_ids_impl(cx.editor, &document_ids, false)
} }
fn force_buffer_close( fn force_buffer_close(
@ -2132,7 +2140,57 @@ pub mod cmd {
args: &[Cow<str>], args: &[Cow<str>],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
buffer_close_impl(cx.editor, args, true) let document_ids = buffer_gather_paths_impl(cx.editor, args);
buffer_close_by_ids_impl(cx.editor, &document_ids, true)
}
fn buffer_gather_others_impl(editor: &mut Editor) -> Vec<DocumentId> {
let current_document = &doc!(editor).id();
editor
.documents()
.map(|doc| doc.id())
.filter(|doc_id| doc_id != current_document)
.collect()
}
fn buffer_close_others(
cx: &mut compositor::Context,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let document_ids = buffer_gather_others_impl(cx.editor);
buffer_close_by_ids_impl(cx.editor, &document_ids, false)
}
fn force_buffer_close_others(
cx: &mut compositor::Context,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let document_ids = buffer_gather_others_impl(cx.editor);
buffer_close_by_ids_impl(cx.editor, &document_ids, true)
}
fn buffer_gather_all_impl(editor: &mut Editor) -> Vec<DocumentId> {
editor.documents().map(|doc| doc.id()).collect()
}
fn buffer_close_all(
cx: &mut compositor::Context,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let document_ids = buffer_gather_all_impl(cx.editor);
buffer_close_by_ids_impl(cx.editor, &document_ids, false)
}
fn force_buffer_close_all(
cx: &mut compositor::Context,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let document_ids = buffer_gather_all_impl(cx.editor);
buffer_close_by_ids_impl(cx.editor, &document_ids, true)
} }
fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> { fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
@ -2970,6 +3028,34 @@ pub mod cmd {
fun: force_buffer_close, fun: force_buffer_close,
completer: Some(completers::buffer), completer: Some(completers::buffer),
}, },
TypableCommand {
name: "buffer-close-others",
aliases: &["bco", "bcloseother"],
doc: "Close all buffers but the currently focused one.",
fun: buffer_close_others,
completer: None,
},
TypableCommand {
name: "buffer-close-others!",
aliases: &["bco!", "bcloseother!"],
doc: "Close all buffers but the currently focused one.",
fun: force_buffer_close_others,
completer: None,
},
TypableCommand {
name: "buffer-close-all",
aliases: &["bca", "bcloseall"],
doc: "Close all buffers, without quiting.",
fun: buffer_close_all,
completer: None,
},
TypableCommand {
name: "buffer-close-all!",
aliases: &["bca!", "bcloseall!"],
doc: "Close all buffers forcefully (ignoring unsaved changes), without quiting.",
fun: force_buffer_close_all,
completer: None,
},
TypableCommand { TypableCommand {
name: "write", name: "write",
aliases: &["w"], aliases: &["w"],