From 78d37fd332ab98e867b8e74f08c4cf45295c167a Mon Sep 17 00:00:00 2001 From: Daniel S Poulin Date: Mon, 28 Feb 2022 20:31:24 -0500 Subject: [PATCH] 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? --- book/src/generated/typable-cmd.md | 4 + helix-term/src/commands.rs | 120 +++++++++++++++++++++++++----- 2 files changed, 107 insertions(+), 17 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 5881716d..370da21a 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -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. | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2c5a2d90..74d4c8d7 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2077,18 +2077,27 @@ pub mod cmd { Ok(()) } - fn buffer_close_impl( + fn buffer_close_by_ids_impl( editor: &mut Editor, - args: &[Cow], + 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]) -> Vec { + // 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], _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], _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 { + 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], + _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], + _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 { + editor.documents().map(|doc| doc.id()).collect() + } + + fn buffer_close_all( + cx: &mut compositor::Context, + _args: &[Cow], + _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], + _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>) -> 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"],