Skip to content

Commit 1d8bb22

Browse files
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 <[email protected]> * Make it work with buffer-close-all and the like * Cleanup Use Cow instead of String, and rename DoesntExist -> DoesNotExist Co-authored-by: Poliorcetics <[email protected]>
1 parent 6764744 commit 1d8bb22

File tree

3 files changed

+59
-25
lines changed

3 files changed

+59
-25
lines changed

helix-term/src/commands/typed.rs

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ops::Deref;
22

33
use super::*;
44

5-
use helix_view::editor::{Action, ConfigEvent};
5+
use helix_view::editor::{Action, CloseError, ConfigEvent};
66
use 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.
518540
pub(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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use helix_core::auto_pairs::AutoPairs;
55
use helix_core::Range;
66
use serde::de::{self, Deserialize, Deserializer};
77
use serde::Serialize;
8+
use std::borrow::Cow;
89
use std::cell::Cell;
910
use std::collections::HashMap;
1011
use 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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use 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

3333
pub use helix_core::diagnostic::Severity;
3434
pub 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+
714722
impl 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() {

0 commit comments

Comments
 (0)