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?
imgbot
Daniel S Poulin 3 years ago committed by GitHub
parent 59c691d2db
commit 78d37fd332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,6 +5,10 @@
| `: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 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) |
| `:new`, `:n` | Create a new scratch buffer. |
| `:format`, `:fmt` | Format the file using the LSP formatter. |

@ -2077,18 +2077,27 @@ pub mod cmd {
Ok(())
}
fn buffer_close_impl(
fn buffer_close_by_ids_impl(
editor: &mut Editor,
args: &[Cow<str>],
doc_ids: &[DocumentId],
force: bool,
) -> 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() {
let doc_id = view!(editor).doc;
editor.close_document(doc_id, force)?;
return Ok(());
return vec![doc_id];
}
let mut nonexistent_buffers = vec![];
let mut document_ids = vec![];
for arg in args {
let doc_id = editor.documents().find_map(|doc| {
let arg_path = Some(Path::new(arg.as_ref()));
@ -2102,21 +2111,19 @@ pub mod cmd {
});
match doc_id {
Some(doc_id) => editor.close_document(doc_id, force)?,
None => nonexistent_buffers.push(arg),
Some(doc_id) => document_ids.push(doc_id),
None => nonexistent_buffers.push(format!("'{}'", arg)),
}
}
let nonexistent_buffers: Vec<_> = nonexistent_buffers
.into_iter()
.map(|str| format!("'{}'", str))
.collect();
editor.set_error(format!(
"couldn't close buffer(s) {}: does not exist",
nonexistent_buffers.join(", ")
));
if !nonexistent_buffers.is_empty() {
editor.set_error(format!(
"cannot close non-existent buffers: {}",
nonexistent_buffers.join(", ")
));
}
Ok(())
document_ids
}
fn buffer_close(
@ -2124,7 +2131,8 @@ pub mod cmd {
args: &[Cow<str>],
_event: PromptEvent,
) -> 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(
@ -2132,7 +2140,57 @@ pub mod cmd {
args: &[Cow<str>],
_event: PromptEvent,
) -> 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<()> {
@ -2970,6 +3028,34 @@ pub mod cmd {
fun: force_buffer_close,
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 {
name: "write",
aliases: &["w"],

Loading…
Cancel
Save