on master help sign in
Change focus to modified docs on quit (#3872)
1d8bb22A-Walrus
helix-term/src/commands/typed.rs+39−15
@@ -2,7 +2,7 @@ use std::ops::Deref;
2
2
3use super::*;
3use super::*;
4
4
5−use helix_view::editor::{Action, ConfigEvent};
5+use helix_view::editor::{Action, CloseError, ConfigEvent};
6use ui::completers::{self, Completer};
6use ui::completers::{self, Completer};
7
7
8#[derive(Clone)]
8#[derive(Clone)]
@@ -71,8 +71,29 @@ fn buffer_close_by_ids_impl(
71 doc_ids: &[DocumentId],
71 doc_ids: &[DocumentId],
72 force: bool,
72 force: bool,
73) -> anyhow::Result<()> {
73) -> anyhow::Result<()> {
74− for &doc_id in doc_ids {
74+ let (modified_ids, modified_names): (Vec<_>, Vec<_>) = doc_ids
75+ .iter()
76+ .filter_map(|&doc_id| {
75− editor.close_document(doc_id, force)?;
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+ );
76 }
97 }
77
98
78 Ok(())
99 Ok(())
@@ -513,23 +534,26 @@ fn force_write_quit(
513 force_quit(cx, &[], event)
534 force_quit(cx, &[], event)
514}
535}
515
536
516−/// Results an error if there are modified buffers remaining and sets editor error,
537+/// Results in an error if there are modified buffers remaining and sets editor
517−/// otherwise returns `Ok(())`
538+/// error, otherwise returns `Ok(())`. If the current document is unmodified,
539+/// and there are modified documents, switches focus to one of them.
518pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> {
540pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> {
519− let modified: Vec<_> = editor
541+ let (modified_ids, modified_names): (Vec<_>, Vec<_>) = editor
520 .documents()
542 .documents()
521 .filter(|doc| doc.is_modified())
543 .filter(|doc| doc.is_modified())
522− .map(|doc| {
544+ .map(|doc| (doc.id(), doc.display_name()))
523− doc.relative_path()
545+ .unzip();
524− .map(|path| path.to_string_lossy().to_string())
546+ if let Some(first) = modified_ids.first() {
525− .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into())
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);
526− })
552+ }
527− .collect();
528− if !modified.is_empty() {
529 bail!(
553 bail!(
530 "{} unsaved buffer(s) remaining: {:?}",
554 "{} unsaved buffer(s) remaining: {:?}",
531− modified.len(),
555+ modified_names.len(),
532− modified
556+ modified_names
533 );
557 );
534 }
558 }
535 Ok(())
559 Ok(())
helix-view/src/document.rs+7
@@ -5,6 +5,7 @@ use helix_core::auto_pairs::AutoPairs;
5use helix_core::Range;
5use helix_core::Range;
6use serde::de::{self, Deserialize, Deserializer};
6use serde::de::{self, Deserialize, Deserializer};
7use serde::Serialize;
7use serde::Serialize;
8+use std::borrow::Cow;
8use std::cell::Cell;
9use std::cell::Cell;
9use std::collections::HashMap;
10use std::collections::HashMap;
10use std::fmt::Display;
11use std::fmt::Display;
@@ -1038,6 +1039,12 @@ impl Document {
1038 .map(helix_core::path::get_relative_path)
1039 .map(helix_core::path::get_relative_path)
1039 }
1040 }
1040
1041
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+
1041 // transact(Fn) ?
1048 // transact(Fn) ?
1042
1049
1043 // -- LSP methods
1050 // -- LSP methods
helix-view/src/editor.rs+13−10
@@ -1,6 +1,6 @@
1use crate::{
1use crate::{
2 clipboard::{get_clipboard_provider, ClipboardProvider},
2 clipboard::{get_clipboard_provider, ClipboardProvider},
3− document::{Mode, SCRATCH_BUFFER_NAME},
3+ document::Mode,
4 graphics::{CursorKind, Rect},
4 graphics::{CursorKind, Rect},
5 info::Info,
5 info::Info,
6 input::KeyEvent,
6 input::KeyEvent,
@@ -28,7 +28,7 @@ use tokio::{
28 time::{sleep, Duration, Instant, Sleep},
28 time::{sleep, Duration, Instant, Sleep},
29};
29};
30
30
31−use anyhow::{bail, Error};
31+use anyhow::Error;
32
32
33pub use helix_core::diagnostic::Severity;
33pub use helix_core::diagnostic::Severity;
34pub use helix_core::register::Registers;
34pub use helix_core::register::Registers;
@@ -711,6 +711,14 @@ pub enum Action {
711 VerticalSplit,
711 VerticalSplit,
712}
712}
713
713
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+
714impl Editor {
722impl Editor {
715 pub fn new(
723 pub fn new(
716 mut area: Rect,
724 mut area: Rect,
@@ -1070,19 +1078,14 @@ impl Editor {
1070 self._refresh();
1078 self._refresh();
1071 }
1079 }
1072
1080
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> {
1074 let doc = match self.documents.get(&doc_id) {
1082 let doc = match self.documents.get(&doc_id) {
1075 Some(doc) => doc,
1083 Some(doc) => doc,
1076− None => bail!("document does not exist"),
1084+ None => return Err(CloseError::DoesNotExist),
1077 };
1085 };
1078
1086
1079 if !force && doc.is_modified() {
1087 if !force && doc.is_modified() {
1080− bail!(
1088+ return Err(CloseError::BufferModified(doc.display_name().into_owned()));
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− );
1086 }
1089 }
1087
1090
1088 if let Some(language_server) = doc.language_server() {
1091 if let Some(language_server) = doc.language_server() {