diff options
author | Joscha <joscha@plugh.de> | 2022-09-25 18:54:56 +0200 |
---|---|---|
committer | Joscha <joscha@plugh.de> | 2022-09-25 18:57:59 +0200 |
commit | 9aac9f6fdd66c4f4eee3c3191a1338e504168f44 (patch) | |
tree | fac174ad3012db6222be762332ebe217967bb2a6 | |
parent | 4c7ac31699c6db2dbcb905d44329c572dbb2d68a (diff) |
Add error popup when external editor fails
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/ui/chat.rs | 2 | ||||
-rw-r--r-- | src/ui/chat/tree.rs | 11 | ||||
-rw-r--r-- | src/ui/euph/account.rs | 10 | ||||
-rw-r--r-- | src/ui/euph/auth.rs | 15 | ||||
-rw-r--r-- | src/ui/euph/nick.rs | 15 | ||||
-rw-r--r-- | src/ui/euph/room.rs | 15 | ||||
-rw-r--r-- | src/ui/rooms.rs | 17 | ||||
-rw-r--r-- | src/ui/util.rs | 68 | ||||
-rw-r--r-- | src/ui/widgets/editor.rs | 31 |
10 files changed, 95 insertions, 90 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fa35b4..3cffe55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Procedure when bumping the version number: ### Added - Room deletion confirmation popup - Message inspection popup +- Error popup when external editor fails ### Fixed - Cursor being visible through popups diff --git a/src/ui/chat.rs b/src/ui/chat.rs index 6d6514f..467307d 100644 --- a/src/ui/chat.rs +++ b/src/ui/chat.rs @@ -5,6 +5,7 @@ mod blocks; mod tree; +use std::io; use std::sync::Arc; use async_trait::async_trait; @@ -79,6 +80,7 @@ pub enum Reaction<M: Msg> { parent: Option<M::Id>, content: String, }, + ComposeError(io::Error), } impl<M: Msg> Reaction<M> { diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index 2ea917f..7fdbc5c 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -225,7 +225,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> { fn list_editor_key_bindings(&self, bindings: &mut KeyBindingsList) { bindings.binding("esc", "close editor"); bindings.binding("enter", "send message"); - util::list_editor_key_bindings(bindings, |_| true, true); + util::list_editor_key_bindings_allowing_external_editing(bindings, |_| true); } fn handle_editor_input_event( @@ -256,16 +256,17 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> { } _ => { - let handled = util::handle_editor_input_event( + let handled = util::handle_editor_input_event_allowing_external_editing( &self.editor, terminal, crossterm_lock, event, |_| true, - true, ); - if !handled { - return Reaction::NotHandled; + match handled { + Ok(true) => {} + Ok(false) => return Reaction::NotHandled, + Err(e) => return Reaction::ComposeError(e), } } } diff --git a/src/ui/euph/account.rs b/src/ui/euph/account.rs index fd16546..0a22479 100644 --- a/src/ui/euph/account.rs +++ b/src/ui/euph/account.rs @@ -1,9 +1,6 @@ -use std::sync::Arc; - use crossterm::style::{ContentStyle, Stylize}; use euphoxide::api::PersonalAccountView; use euphoxide::conn::Status; -use parking_lot::FairMutex; use toss::terminal::Terminal; use crate::euph::Room; @@ -133,7 +130,7 @@ impl AccountUiState { Focus::Password => bindings.binding("enter", "log in"), } bindings.binding("tab", "switch focus"); - util::list_editor_key_bindings(bindings, |c| c != '\n', false); + util::list_editor_key_bindings(bindings, |c| c != '\n'); } Self::LoggedIn(_) => bindings.binding("L", "log out"), } @@ -142,7 +139,6 @@ impl AccountUiState { pub fn handle_input_event( &mut self, terminal: &mut Terminal, - crossterm_lock: &Arc<FairMutex<()>>, event: &InputEvent, room: &Option<Room>, ) -> EventResult { @@ -170,10 +166,8 @@ impl AccountUiState { if util::handle_editor_input_event( &logged_out.email, terminal, - crossterm_lock, event, |c| c != '\n', - false, ) { EventResult::Handled } else { @@ -192,10 +186,8 @@ impl AccountUiState { if util::handle_editor_input_event( &logged_out.password, terminal, - crossterm_lock, event, |c| c != '\n', - false, ) { EventResult::Handled } else { diff --git a/src/ui/euph/auth.rs b/src/ui/euph/auth.rs index 46d83e2..b9b72b9 100644 --- a/src/ui/euph/auth.rs +++ b/src/ui/euph/auth.rs @@ -1,6 +1,3 @@ -use std::sync::Arc; - -use parking_lot::FairMutex; use toss::terminal::Terminal; use crate::euph::Room; @@ -23,7 +20,7 @@ pub fn widget(editor: &EditorState) -> BoxedWidget { pub fn list_key_bindings(bindings: &mut KeyBindingsList) { bindings.binding("esc", "abort"); bindings.binding("enter", "authenticate"); - util::list_editor_key_bindings(bindings, |_| true, false); + util::list_editor_key_bindings(bindings, |_| true); } pub enum EventResult { @@ -34,7 +31,6 @@ pub enum EventResult { pub fn handle_input_event( terminal: &mut Terminal, - crossterm_lock: &Arc<FairMutex<()>>, event: &InputEvent, room: &Option<Room>, editor: &EditorState, @@ -48,14 +44,7 @@ pub fn handle_input_event( EventResult::ResetState } _ => { - if util::handle_editor_input_event( - editor, - terminal, - crossterm_lock, - event, - |_| true, - false, - ) { + if util::handle_editor_input_event(editor, terminal, event, |_| true) { EventResult::Handled } else { EventResult::NotHandled diff --git a/src/ui/euph/nick.rs b/src/ui/euph/nick.rs index 24f7178..9237c57 100644 --- a/src/ui/euph/nick.rs +++ b/src/ui/euph/nick.rs @@ -1,7 +1,4 @@ -use std::sync::Arc; - use euphoxide::conn::Joined; -use parking_lot::FairMutex; use toss::styled::Styled; use toss::terminal::Terminal; @@ -34,7 +31,7 @@ fn nick_char(c: char) -> bool { pub fn list_key_bindings(bindings: &mut KeyBindingsList) { bindings.binding("esc", "abort"); bindings.binding("enter", "set nick"); - util::list_editor_key_bindings(bindings, nick_char, false); + util::list_editor_key_bindings(bindings, nick_char); } pub enum EventResult { @@ -45,7 +42,6 @@ pub enum EventResult { pub fn handle_input_event( terminal: &mut Terminal, - crossterm_lock: &Arc<FairMutex<()>>, event: &InputEvent, room: &Option<Room>, editor: &EditorState, @@ -59,14 +55,7 @@ pub fn handle_input_event( EventResult::ResetState } _ => { - if util::handle_editor_input_event( - editor, - terminal, - crossterm_lock, - event, - nick_char, - false, - ) { + if util::handle_editor_input_event(editor, terminal, event, nick_char) { EventResult::Handled } else { EventResult::NotHandled diff --git a/src/ui/euph/room.rs b/src/ui/euph/room.rs index 9b738ed..4d0d57a 100644 --- a/src/ui/euph/room.rs +++ b/src/ui/euph/room.rs @@ -380,6 +380,13 @@ impl EuphRoom { } return true; } + Reaction::ComposeError(e) => { + self.popups.push_front(RoomPopup::Error { + description: "Failed to use external editor".to_string(), + reason: format!("{e}"), + }); + return true; + } } if self.handle_inspect_initiating_input_event(event).await { @@ -470,8 +477,7 @@ impl EuphRoom { .await } State::Auth(editor) => { - match auth::handle_input_event(terminal, crossterm_lock, event, &self.room, editor) - { + match auth::handle_input_event(terminal, event, &self.room, editor) { auth::EventResult::NotHandled => false, auth::EventResult::Handled => true, auth::EventResult::ResetState => { @@ -481,8 +487,7 @@ impl EuphRoom { } } State::Nick(editor) => { - match nick::handle_input_event(terminal, crossterm_lock, event, &self.room, editor) - { + match nick::handle_input_event(terminal, event, &self.room, editor) { nick::EventResult::NotHandled => false, nick::EventResult::Handled => true, nick::EventResult::ResetState => { @@ -492,7 +497,7 @@ impl EuphRoom { } } State::Account(account) => { - match account.handle_input_event(terminal, crossterm_lock, event, &self.room) { + match account.handle_input_event(terminal, event, &self.room) { account::EventResult::NotHandled => false, account::EventResult::Handled => true, account::EventResult::ResetState => { diff --git a/src/ui/rooms.rs b/src/ui/rooms.rs index c349b39..43f8fbe 100644 --- a/src/ui/rooms.rs +++ b/src/ui/rooms.rs @@ -352,13 +352,13 @@ impl Rooms { bindings.heading("Rooms"); bindings.binding("esc", "abort"); bindings.binding("enter", "connect to room"); - util::list_editor_key_bindings(bindings, Self::room_char, false); + util::list_editor_key_bindings(bindings, Self::room_char); } State::Delete(_, _) => { bindings.heading("Rooms"); bindings.binding("esc", "abort"); bindings.binding("enter", "delete room"); - util::list_editor_key_bindings(bindings, Self::room_char, false); + util::list_editor_key_bindings(bindings, Self::room_char); } } } @@ -470,16 +470,7 @@ impl Rooms { self.state = State::ShowRoom(name); } } - _ => { - return util::handle_editor_input_event( - ed, - terminal, - crossterm_lock, - event, - Self::room_char, - false, - ) - } + _ => return util::handle_editor_input_event(ed, terminal, event, Self::room_char), }, State::Delete(name, editor) => match event { key!(Esc) => self.state = State::ShowList, @@ -492,10 +483,8 @@ impl Rooms { return util::handle_editor_input_event( editor, terminal, - crossterm_lock, event, Self::room_char, - false, ) } }, diff --git a/src/ui/util.rs b/src/ui/util.rs index 6b58796..a5d6df0 100644 --- a/src/ui/util.rs +++ b/src/ui/util.rs @@ -1,3 +1,4 @@ +use std::io; use std::sync::Arc; use parking_lot::FairMutex; @@ -10,7 +11,7 @@ pub fn prompt( terminal: &mut Terminal, crossterm_lock: &Arc<FairMutex<()>>, initial_text: &str, -) -> Option<String> { +) -> io::Result<String> { let content = { let _guard = crossterm_lock.lock(); terminal.suspend().expect("could not suspend"); @@ -19,38 +20,23 @@ pub fn prompt( content }; - // TODO Don't swipe this error under the rug - let content = content.ok()?; - - if content.trim().is_empty() { - None - } else { - Some(content) - } + content } -// TODO List key binding util functions - -pub fn list_editor_key_bindings( +fn list_editor_editing_key_bindings( bindings: &mut KeyBindingsList, char_filter: impl Fn(char) -> bool, - can_edit_externally: bool, ) { if char_filter('\n') { bindings.binding("enter+<any modifier>", "insert newline"); } - // Editing bindings.binding("ctrl+h, backspace", "delete before cursor"); bindings.binding("ctrl+d, delete", "delete after cursor"); bindings.binding("ctrl+l", "clear editor contents"); - if can_edit_externally { - bindings.binding("ctrl+x", "edit in external editor"); - } - - bindings.empty(); +} - // Cursor movement +fn list_editor_cursor_movement_key_bindings(bindings: &mut KeyBindingsList) { bindings.binding("ctrl+b, ←", "move cursor left"); bindings.binding("ctrl+f, →", "move cursor right"); bindings.binding("alt+b, ctrl+←", "move cursor left a word"); @@ -60,13 +46,20 @@ pub fn list_editor_key_bindings( bindings.binding("↑/↓", "move cursor up/down"); } +pub fn list_editor_key_bindings( + bindings: &mut KeyBindingsList, + char_filter: impl Fn(char) -> bool, +) { + list_editor_editing_key_bindings(bindings, char_filter); + bindings.empty(); + list_editor_cursor_movement_key_bindings(bindings); +} + pub fn handle_editor_input_event( editor: &EditorState, terminal: &mut Terminal, - crossterm_lock: &Arc<FairMutex<()>>, event: &InputEvent, char_filter: impl Fn(char) -> bool, - can_edit_externally: bool, ) -> bool { match event { // Enter with *any* modifier pressed - if ctrl and shift don't @@ -94,7 +87,6 @@ pub fn handle_editor_input_event( key!(Ctrl + 'h') | key!(Backspace) => editor.backspace(terminal.frame()), key!(Ctrl + 'd') | key!(Delete) => editor.delete(), key!(Ctrl + 'l') => editor.clear(), - key!(Ctrl + 'x') if can_edit_externally => editor.edit_externally(terminal, crossterm_lock), // TODO Key bindings to delete words // Cursor movement @@ -112,3 +104,33 @@ pub fn handle_editor_input_event( true } + +pub fn list_editor_key_bindings_allowing_external_editing( + bindings: &mut KeyBindingsList, + char_filter: impl Fn(char) -> bool, +) { + list_editor_editing_key_bindings(bindings, char_filter); + bindings.binding("ctrl+x", "edit in external editor"); + bindings.empty(); + list_editor_cursor_movement_key_bindings(bindings); +} + +pub fn handle_editor_input_event_allowing_external_editing( + editor: &EditorState, + terminal: &mut Terminal, + crossterm_lock: &Arc<FairMutex<()>>, + event: &InputEvent, + char_filter: impl Fn(char) -> bool, +) -> io::Result<bool> { + if let key!(Ctrl + 'x') = event { + editor.edit_externally(terminal, crossterm_lock)?; + Ok(true) + } else { + Ok(handle_editor_input_event( + editor, + terminal, + event, + char_filter, + )) + } +} diff --git a/src/ui/widgets/editor.rs b/src/ui/widgets/editor.rs index c90f6ee..1aecd93 100644 --- a/src/ui/widgets/editor.rs +++ b/src/ui/widgets/editor.rs @@ -1,5 +1,5 @@ -use std::iter; use std::sync::Arc; +use std::{io, iter}; use async_trait::async_trait; use crossterm::style::{ContentStyle, Stylize}; @@ -404,15 +404,30 @@ impl EditorState { self.0.lock().move_cursor_down(frame); } - pub fn edit_externally(&self, terminal: &mut Terminal, crossterm_lock: &Arc<FairMutex<()>>) { + pub fn edit_externally( + &self, + terminal: &mut Terminal, + crossterm_lock: &Arc<FairMutex<()>>, + ) -> io::Result<()> { let mut guard = self.0.lock(); - if let Some(text) = util::prompt(terminal, crossterm_lock, &guard.text) { - if let Some(text) = text.strip_suffix('\n') { - guard.set_text(terminal.frame(), text.to_string()); - } else { - guard.set_text(terminal.frame(), text); - } + let text = util::prompt(terminal, crossterm_lock, &guard.text)?; + + if text.trim().is_empty() { + // The user likely wanted to abort the edit and has deleted the + // entire text (bar whitespace left over by some editors). + return Ok(()); } + + if let Some(text) = text.strip_suffix('\n') { + // Some editors like vim add a trailing newline that would look out + // of place in cove's editor. To intentionally add a trailing + // newline, simply add two in-editor. + guard.set_text(terminal.frame(), text.to_string()); + } else { + guard.set_text(terminal.frame(), text); + } + + Ok(()) } } |