on master help sign in
Implement bulk buffer closing commands (#1677)
78d37fdDaniel S Poulin
book/src/generated/typable-cmd.md+4
@@ -5,6 +5,10 @@
5| `:open`, `:o` | Open a file from disk into the current view. |
5| `:open`, `:o` | Open a file from disk into the current view. |
6| `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. |
6| `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. |
7| `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully (ignoring unsaved changes). |
7| `: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. |
8| `:write`, `:w` | Write changes to disk. Accepts an optional path (:write some/path.txt) |
12| `:write`, `:w` | Write changes to disk. Accepts an optional path (:write some/path.txt) |
9| `:new`, `:n` | Create a new scratch buffer. |
13| `:new`, `:n` | Create a new scratch buffer. |
10| `:format`, `:fmt` | Format the file using the LSP formatter. |
14| `:format`, `:fmt` | Format the file using the LSP formatter. |
helix-term/src/commands.rs+103−17
@@ -2077,18 +2077,27 @@ pub mod cmd {
2077 Ok(())
2077 Ok(())
2078 }
2078 }
2079
2079
2080− fn buffer_close_impl(
2080+ fn buffer_close_by_ids_impl(
2081 editor: &mut Editor,
2081 editor: &mut Editor,
2082− args: &[Cow<str>],
2082+ doc_ids: &[DocumentId],
2083 force: bool,
2083 force: bool,
2084 ) -> anyhow::Result<()> {
2084 ) -> 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
2085 if args.is_empty() {
2094 if args.is_empty() {
2086 let doc_id = view!(editor).doc;
2095 let doc_id = view!(editor).doc;
2087− editor.close_document(doc_id, force)?;
2088− return Ok(());
2096+ return vec![doc_id];
2089 }
2097 }
2090
2098
2091 let mut nonexistent_buffers = vec![];
2099 let mut nonexistent_buffers = vec![];
2100+ let mut document_ids = vec![];
2092 for arg in args {
2101 for arg in args {
2093 let doc_id = editor.documents().find_map(|doc| {
2102 let doc_id = editor.documents().find_map(|doc| {
2094 let arg_path = Some(Path::new(arg.as_ref()));
2103 let arg_path = Some(Path::new(arg.as_ref()));
@@ -2102,21 +2111,19 @@ pub mod cmd {
2102 });
2111 });
2103
2112
2104 match doc_id {
2113 match doc_id {
2105− Some(doc_id) => editor.close_document(doc_id, force)?,
2114+ Some(doc_id) => document_ids.push(doc_id),
2106− None => nonexistent_buffers.push(arg),
2115+ None => nonexistent_buffers.push(format!("'{}'", arg)),
2107 }
2116 }
2108 }
2117 }
2109
2118
2110− let nonexistent_buffers: Vec<_> = nonexistent_buffers
2119+ if !nonexistent_buffers.is_empty() {
2111− .into_iter()
2112− .map(|str| format!("'{}'", str))
2113− .collect();
2114− editor.set_error(format!(
2120+ editor.set_error(format!(
2115− "couldn't close buffer(s) {}: does not exist",
2121+ "cannot close non-existent buffers: {}",
2116− nonexistent_buffers.join(", ")
2122+ nonexistent_buffers.join(", ")
2117− ));
2123+ ));
2124+ }
2118
2125
2119− Ok(())
2126+ document_ids
2120 }
2127 }
2121
2128
2122 fn buffer_close(
2129 fn buffer_close(
@@ -2124,7 +2131,8 @@ pub mod cmd {
2124 args: &[Cow<str>],
2131 args: &[Cow<str>],
2125 _event: PromptEvent,
2132 _event: PromptEvent,
2126 ) -> anyhow::Result<()> {
2133 ) -> 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)
2128 }
2136 }
2129
2137
2130 fn force_buffer_close(
2138 fn force_buffer_close(
@@ -2132,7 +2140,57 @@ pub mod cmd {
2132 args: &[Cow<str>],
2140 args: &[Cow<str>],
2133 _event: PromptEvent,
2141 _event: PromptEvent,
2134 ) -> anyhow::Result<()> {
2142 ) -> 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)
2136 }
2194 }
2137
2195
2138 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
2196 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
@@ -2970,6 +3028,34 @@ pub mod cmd {
2970 fun: force_buffer_close,
3028 fun: force_buffer_close,
2971 completer: Some(completers::buffer),
3029 completer: Some(completers::buffer),
2972 },
3030 },
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+ },
2973 TypableCommand {
3059 TypableCommand {
2974 name: "write",
3060 name: "write",
2975 aliases: &["w"],
3061 aliases: &["w"],