on master help sign in
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?

commit78d37fd332ab98e867b8e74f08c4cf45295c167a authorDaniel S Poulin <dan.poulin@digitalsurgeons.com> dateMon 28 Feb 2022, 20:31 (5 years ago) parent59c691d
2 files changed+107−17
book/src/generated/typable-cmd.md+4 helix-term/src/commands.rs+103−17
book/src/generated/typable-cmd.md+4
@@ -5,6 +5,10 @@
55| `:open`, `:o` | Open a file from disk into the current view. |
66| `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. |
77| `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully (ignoring unsaved changes). |
8+| `:buffer-close-others`, `:bco`, `:bcloseother` | Close all buffers but the currently focused one. |
9+| `:buffer-close-others!`, `:bco!`, `:bcloseother!` | Close all buffers but the currently focused one. |
10+| `:buffer-close-all`, `:bca`, `:bcloseall` | Close all buffers, without quiting. |
11+| `:buffer-close-all!`, `:bca!`, `:bcloseall!` | Close all buffers forcefully (ignoring unsaved changes), without quiting. |
812| `:write`, `:w` | Write changes to disk. Accepts an optional path (:write some/path.txt) |
913| `:new`, `:n` | Create a new scratch buffer. |
1014| `:format`, `:fmt` | Format the file using the LSP formatter. |
helix-term/src/commands.rs+103−17
@@ -2077,18 +2077,27 @@ pub mod cmd {
20772077 Ok(())
20782078 }
20792079
2080− fn buffer_close_impl(
2080+ fn buffer_close_by_ids_impl(
20812081 editor: &mut Editor,
2082− args: &[Cow<str>],
2082+ doc_ids: &[DocumentId],
20832083 force: bool,
20842084 ) -> anyhow::Result<()> {
2085+ for &doc_id in doc_ids {
2086+ editor.close_document(doc_id, force)?;
2087+ }
2088+
2089+ Ok(())
2090+ }
2091+
2092+ fn buffer_gather_paths_impl(editor: &mut Editor, args: &[Cow<str>]) -> Vec<DocumentId> {
2093+ // No arguments implies current document
20852094 if args.is_empty() {
20862095 let doc_id = view!(editor).doc;
2087− editor.close_document(doc_id, force)?;
2088− return Ok(());
2096+ return vec![doc_id];
20892097 }
20902098
20912099 let mut nonexistent_buffers = vec![];
2100+ let mut document_ids = vec![];
20922101 for arg in args {
20932102 let doc_id = editor.documents().find_map(|doc| {
20942103 let arg_path = Some(Path::new(arg.as_ref()));
@@ -2102,21 +2111,19 @@ pub mod cmd {
21022111 });
21032112
21042113 match doc_id {
2105− Some(doc_id) => editor.close_document(doc_id, force)?,
2106− None => nonexistent_buffers.push(arg),
2114+ Some(doc_id) => document_ids.push(doc_id),
2115+ None => nonexistent_buffers.push(format!("'{}'", arg)),
21072116 }
21082117 }
21092118
2110− let nonexistent_buffers: Vec<_> = nonexistent_buffers
2111− .into_iter()
2112− .map(|str| format!("'{}'", str))
2113− .collect();
2114− editor.set_error(format!(
2115− "couldn't close buffer(s) {}: does not exist",
2116− nonexistent_buffers.join(", ")
2117− ));
2119+ if !nonexistent_buffers.is_empty() {
2120+ editor.set_error(format!(
2121+ "cannot close non-existent buffers: {}",
2122+ nonexistent_buffers.join(", ")
2123+ ));
2124+ }
21182125
2119− Ok(())
2126+ document_ids
21202127 }
21212128
21222129 fn buffer_close(
@@ -2124,7 +2131,8 @@ pub mod cmd {
21242131 args: &[Cow<str>],
21252132 _event: PromptEvent,
21262133 ) -> anyhow::Result<()> {
2127− buffer_close_impl(cx.editor, args, false)
2134+ let document_ids = buffer_gather_paths_impl(cx.editor, args);
2135+ buffer_close_by_ids_impl(cx.editor, &document_ids, false)
21282136 }
21292137
21302138 fn force_buffer_close(
@@ -2132,7 +2140,57 @@ pub mod cmd {
21322140 args: &[Cow<str>],
21332141 _event: PromptEvent,
21342142 ) -> anyhow::Result<()> {
2135− buffer_close_impl(cx.editor, args, true)
2143+ let document_ids = buffer_gather_paths_impl(cx.editor, args);
2144+ buffer_close_by_ids_impl(cx.editor, &document_ids, true)
2145+ }
2146+
2147+ fn buffer_gather_others_impl(editor: &mut Editor) -> Vec<DocumentId> {
2148+ let current_document = &doc!(editor).id();
2149+ editor
2150+ .documents()
2151+ .map(|doc| doc.id())
2152+ .filter(|doc_id| doc_id != current_document)
2153+ .collect()
2154+ }
2155+
2156+ fn buffer_close_others(
2157+ cx: &mut compositor::Context,
2158+ _args: &[Cow<str>],
2159+ _event: PromptEvent,
2160+ ) -> anyhow::Result<()> {
2161+ let document_ids = buffer_gather_others_impl(cx.editor);
2162+ buffer_close_by_ids_impl(cx.editor, &document_ids, false)
2163+ }
2164+
2165+ fn force_buffer_close_others(
2166+ cx: &mut compositor::Context,
2167+ _args: &[Cow<str>],
2168+ _event: PromptEvent,
2169+ ) -> anyhow::Result<()> {
2170+ let document_ids = buffer_gather_others_impl(cx.editor);
2171+ buffer_close_by_ids_impl(cx.editor, &document_ids, true)
2172+ }
2173+
2174+ fn buffer_gather_all_impl(editor: &mut Editor) -> Vec<DocumentId> {
2175+ editor.documents().map(|doc| doc.id()).collect()
2176+ }
2177+
2178+ fn buffer_close_all(
2179+ cx: &mut compositor::Context,
2180+ _args: &[Cow<str>],
2181+ _event: PromptEvent,
2182+ ) -> anyhow::Result<()> {
2183+ let document_ids = buffer_gather_all_impl(cx.editor);
2184+ buffer_close_by_ids_impl(cx.editor, &document_ids, false)
2185+ }
2186+
2187+ fn force_buffer_close_all(
2188+ cx: &mut compositor::Context,
2189+ _args: &[Cow<str>],
2190+ _event: PromptEvent,
2191+ ) -> anyhow::Result<()> {
2192+ let document_ids = buffer_gather_all_impl(cx.editor);
2193+ buffer_close_by_ids_impl(cx.editor, &document_ids, true)
21362194 }
21372195
21382196 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
@@ -2970,6 +3028,34 @@ pub mod cmd {
29703028 fun: force_buffer_close,
29713029 completer: Some(completers::buffer),
29723030 },
3031+ TypableCommand {
3032+ name: "buffer-close-others",
3033+ aliases: &["bco", "bcloseother"],
3034+ doc: "Close all buffers but the currently focused one.",
3035+ fun: buffer_close_others,
3036+ completer: None,
3037+ },
3038+ TypableCommand {
3039+ name: "buffer-close-others!",
3040+ aliases: &["bco!", "bcloseother!"],
3041+ doc: "Close all buffers but the currently focused one.",
3042+ fun: force_buffer_close_others,
3043+ completer: None,
3044+ },
3045+ TypableCommand {
3046+ name: "buffer-close-all",
3047+ aliases: &["bca", "bcloseall"],
3048+ doc: "Close all buffers, without quiting.",
3049+ fun: buffer_close_all,
3050+ completer: None,
3051+ },
3052+ TypableCommand {
3053+ name: "buffer-close-all!",
3054+ aliases: &["bca!", "bcloseall!"],
3055+ doc: "Close all buffers forcefully (ignoring unsaved changes), without quiting.",
3056+ fun: force_buffer_close_all,
3057+ completer: None,
3058+ },
29733059 TypableCommand {
29743060 name: "write",
29753061 aliases: &["w"],