on master help sign in
Change focus to modified docs on quit (#3872)

* Change focus to modified docs on quit When quitting with modified documents, automatically switch focus to one of them. * Update helix-term/src/commands/typed.rs Co-authored-by: Poliorcetics <poliorcetics@users.noreply.github.com> * Make it work with buffer-close-all and the like * Cleanup Use Cow instead of String, and rename DoesntExist -> DoesNotExist Co-authored-by: Poliorcetics <poliorcetics@users.noreply.github.com>

commit1d8bb2249b7153b2e9d3591534bdca32176c9975 authorA-Walrus <58790821+A-Walrus@users.noreply.github.com> dateMon 3 Oct 2022, 18:07 (4 years ago) parent6764744
3 files changed+59−25
helix-term/src/commands/typed.rs+39−15 helix-view/src/document.rs+7 helix-view/src/editor.rs+13−10
helix-term/src/commands/typed.rs+39−15
@@ -2,7 +2,7 @@ use std::ops::Deref;
22
33use super::*;
44
5−use helix_view::editor::{Action, ConfigEvent};
5+use helix_view::editor::{Action, CloseError, ConfigEvent};
66use ui::completers::{self, Completer};
77
88#[derive(Clone)]
@@ -71,8 +71,29 @@ fn buffer_close_by_ids_impl(
7171 doc_ids: &[DocumentId],
7272 force: bool,
7373) -> anyhow::Result<()> {
74− for &doc_id in doc_ids {
75− editor.close_document(doc_id, force)?;
74+ let (modified_ids, modified_names): (Vec<_>, Vec<_>) = doc_ids
75+ .iter()
76+ .filter_map(|&doc_id| {
77+ if let Err(CloseError::BufferModified(name)) = editor.close_document(doc_id, force) {
78+ Some((doc_id, name))
79+ } else {
80+ None
81+ }
82+ })
83+ .unzip();
84+
85+ if let Some(first) = modified_ids.first() {
86+ let current = doc!(editor);
87+ // If the current document is unmodified, and there are modified
88+ // documents, switch focus to the first modified doc.
89+ if !modified_ids.contains(&current.id()) {
90+ editor.switch(*first, Action::Replace);
91+ }
92+ bail!(
93+ "{} unsaved buffer(s) remaining: {:?}",
94+ modified_names.len(),
95+ modified_names
96+ );
7697 }
7798
7899 Ok(())
@@ -513,23 +534,26 @@ fn force_write_quit(
513534 force_quit(cx, &[], event)
514535}
515536
516−/// Results an error if there are modified buffers remaining and sets editor error,
517−/// otherwise returns `Ok(())`
537+/// Results in an error if there are modified buffers remaining and sets editor
538+/// error, otherwise returns `Ok(())`. If the current document is unmodified,
539+/// and there are modified documents, switches focus to one of them.
518540pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> {
519− let modified: Vec<_> = editor
541+ let (modified_ids, modified_names): (Vec<_>, Vec<_>) = editor
520542 .documents()
521543 .filter(|doc| doc.is_modified())
522− .map(|doc| {
523− doc.relative_path()
524− .map(|path| path.to_string_lossy().to_string())
525− .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into())
526− })
527− .collect();
528− if !modified.is_empty() {
544+ .map(|doc| (doc.id(), doc.display_name()))
545+ .unzip();
546+ if let Some(first) = modified_ids.first() {
547+ let current = doc!(editor);
548+ // If the current document is unmodified, and there are modified
549+ // documents, switch focus to the first modified doc.
550+ if !modified_ids.contains(&current.id()) {
551+ editor.switch(*first, Action::Replace);
552+ }
529553 bail!(
530554 "{} unsaved buffer(s) remaining: {:?}",
531− modified.len(),
532− modified
555+ modified_names.len(),
556+ modified_names
533557 );
534558 }
535559 Ok(())
helix-view/src/document.rs+7
@@ -5,6 +5,7 @@ use helix_core::auto_pairs::AutoPairs;
55use helix_core::Range;
66use serde::de::{self, Deserialize, Deserializer};
77use serde::Serialize;
8+use std::borrow::Cow;
89use std::cell::Cell;
910use std::collections::HashMap;
1011use std::fmt::Display;
@@ -1038,6 +1039,12 @@ impl Document {
10381039 .map(helix_core::path::get_relative_path)
10391040 }
10401041
1042+ pub fn display_name(&self) -> Cow<'static, str> {
1043+ self.relative_path()
1044+ .map(|path| path.to_string_lossy().to_string().into())
1045+ .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into())
1046+ }
1047+
10411048 // transact(Fn) ?
10421049
10431050 // -- LSP methods
helix-view/src/editor.rs+13−10
@@ -1,6 +1,6 @@
11use crate::{
22 clipboard::{get_clipboard_provider, ClipboardProvider},
3− document::{Mode, SCRATCH_BUFFER_NAME},
3+ document::Mode,
44 graphics::{CursorKind, Rect},
55 info::Info,
66 input::KeyEvent,
@@ -28,7 +28,7 @@ use tokio::{
2828 time::{sleep, Duration, Instant, Sleep},
2929};
3030
31−use anyhow::{bail, Error};
31+use anyhow::Error;
3232
3333pub use helix_core::diagnostic::Severity;
3434pub use helix_core::register::Registers;
@@ -711,6 +711,14 @@ pub enum Action {
711711 VerticalSplit,
712712}
713713
714+/// Error thrown on failed document closed
715+pub enum CloseError {
716+ /// Document doesn't exist
717+ DoesNotExist,
718+ /// Buffer is modified
719+ BufferModified(String),
720+}
721+
714722impl Editor {
715723 pub fn new(
716724 mut area: Rect,
@@ -1070,19 +1078,14 @@ impl Editor {
10701078 self._refresh();
10711079 }
10721080
1073− pub fn close_document(&mut self, doc_id: DocumentId, force: bool) -> anyhow::Result<()> {
1081+ pub fn close_document(&mut self, doc_id: DocumentId, force: bool) -> Result<(), CloseError> {
10741082 let doc = match self.documents.get(&doc_id) {
10751083 Some(doc) => doc,
1076− None => bail!("document does not exist"),
1084+ None => return Err(CloseError::DoesNotExist),
10771085 };
10781086
10791087 if !force && doc.is_modified() {
1080− bail!(
1081− "buffer {:?} is modified",
1082− doc.relative_path()
1083− .map(|path| path.to_string_lossy().to_string())
1084− .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into())
1085− );
1088+ return Err(CloseError::BufferModified(doc.display_name().into_owned()));
10861089 }
10871090
10881091 if let Some(language_server) = doc.language_server() {