chore: introduce AppEventSender to help fix clippy warnings and update to Rust 1.87 (#948)
Moving to Rust 1.87 introduced a clippy warning that `SendError<AppEvent>` was too large. In practice, the only thing we ever did when we got this error was log it (if the mspc channel is closed, then the app is likely shutting down or something, so there's not much to do...), so this finally motivated me to introduce `AppEventSender`, which wraps `std::sync::mpsc::Sender<AppEvent>` with a `send()` method that invokes `send()` on the underlying `Sender` and logs an `Err` if it gets one. This greatly simplifies the code, as many functions that previously returned `Result<(), SendError<AppEvent>>` now return `()`, so we don't have to propagate an `Err` all over the place that we don't really handle, anyway. This also makes it so we can upgrade to Rust 1.87 in CI.
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
use std::sync::mpsc::SendError;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::widgets::WidgetRef;
|
||||
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::user_approval_widget::ApprovalRequest;
|
||||
use crate::user_approval_widget::UserApprovalWidget;
|
||||
|
||||
@@ -17,11 +14,11 @@ use super::BottomPaneView;
|
||||
pub(crate) struct ApprovalModalView<'a> {
|
||||
current: UserApprovalWidget<'a>,
|
||||
queue: Vec<ApprovalRequest>,
|
||||
app_event_tx: Sender<AppEvent>,
|
||||
app_event_tx: AppEventSender,
|
||||
}
|
||||
|
||||
impl ApprovalModalView<'_> {
|
||||
pub fn new(request: ApprovalRequest, app_event_tx: Sender<AppEvent>) -> Self {
|
||||
pub fn new(request: ApprovalRequest, app_event_tx: AppEventSender) -> Self {
|
||||
Self {
|
||||
current: UserApprovalWidget::new(request, app_event_tx.clone()),
|
||||
queue: Vec::new(),
|
||||
@@ -44,14 +41,9 @@ impl ApprovalModalView<'_> {
|
||||
}
|
||||
|
||||
impl<'a> BottomPaneView<'a> for ApprovalModalView<'a> {
|
||||
fn handle_key_event(
|
||||
&mut self,
|
||||
_pane: &mut BottomPane<'a>,
|
||||
key_event: KeyEvent,
|
||||
) -> Result<(), SendError<AppEvent>> {
|
||||
self.current.handle_key_event(key_event)?;
|
||||
fn handle_key_event(&mut self, _pane: &mut BottomPane<'a>, key_event: KeyEvent) {
|
||||
self.current.handle_key_event(key_event);
|
||||
self.maybe_advance();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_complete(&self) -> bool {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::user_approval_widget::ApprovalRequest;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use std::sync::mpsc::SendError;
|
||||
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::user_approval_widget::ApprovalRequest;
|
||||
|
||||
use super::BottomPane;
|
||||
|
||||
@@ -18,11 +15,7 @@ pub(crate) enum ConditionalUpdate {
|
||||
pub(crate) trait BottomPaneView<'a> {
|
||||
/// Handle a key event while the view is active. A redraw is always
|
||||
/// scheduled after this call.
|
||||
fn handle_key_event(
|
||||
&mut self,
|
||||
pane: &mut BottomPane<'a>,
|
||||
key_event: KeyEvent,
|
||||
) -> Result<(), SendError<AppEvent>>;
|
||||
fn handle_key_event(&mut self, _pane: &mut BottomPane<'a>, _key_event: KeyEvent) {}
|
||||
|
||||
/// Return `true` if the view has finished and should be removed.
|
||||
fn is_complete(&self) -> bool {
|
||||
|
||||
@@ -13,9 +13,8 @@ use tui_textarea::Input;
|
||||
use tui_textarea::Key;
|
||||
use tui_textarea::TextArea;
|
||||
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
|
||||
use super::command_popup::CommandPopup;
|
||||
|
||||
@@ -33,11 +32,11 @@ pub enum InputResult {
|
||||
pub(crate) struct ChatComposer<'a> {
|
||||
textarea: TextArea<'a>,
|
||||
command_popup: Option<CommandPopup>,
|
||||
app_event_tx: Sender<AppEvent>,
|
||||
app_event_tx: AppEventSender,
|
||||
}
|
||||
|
||||
impl ChatComposer<'_> {
|
||||
pub fn new(has_input_focus: bool, app_event_tx: Sender<AppEvent>) -> Self {
|
||||
pub fn new(has_input_focus: bool, app_event_tx: AppEventSender) -> Self {
|
||||
let mut textarea = TextArea::default();
|
||||
textarea.set_placeholder_text("send a message");
|
||||
textarea.set_cursor_line_style(ratatui::style::Style::default());
|
||||
@@ -113,9 +112,7 @@ impl ChatComposer<'_> {
|
||||
} => {
|
||||
if let Some(cmd) = popup.selected_command() {
|
||||
// Send command to the app layer.
|
||||
if let Err(e) = self.app_event_tx.send(AppEvent::DispatchCommand(*cmd)) {
|
||||
tracing::error!("failed to send DispatchCommand event: {e}");
|
||||
}
|
||||
self.app_event_tx.send(AppEvent::DispatchCommand(*cmd));
|
||||
|
||||
// Clear textarea so no residual text remains.
|
||||
self.textarea.select_all();
|
||||
|
||||
@@ -6,10 +6,9 @@ use crossterm::event::KeyEvent;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::widgets::WidgetRef;
|
||||
use std::sync::mpsc::SendError;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::user_approval_widget::ApprovalRequest;
|
||||
|
||||
mod approval_modal_view;
|
||||
@@ -33,13 +32,13 @@ pub(crate) struct BottomPane<'a> {
|
||||
/// If present, this is displayed instead of the `composer`.
|
||||
active_view: Option<Box<dyn BottomPaneView<'a> + 'a>>,
|
||||
|
||||
app_event_tx: Sender<AppEvent>,
|
||||
app_event_tx: AppEventSender,
|
||||
has_input_focus: bool,
|
||||
is_task_running: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct BottomPaneParams {
|
||||
pub(crate) app_event_tx: Sender<AppEvent>,
|
||||
pub(crate) app_event_tx: AppEventSender,
|
||||
pub(crate) has_input_focus: bool,
|
||||
}
|
||||
|
||||
@@ -55,12 +54,9 @@ impl BottomPane<'_> {
|
||||
}
|
||||
|
||||
/// Forward a key event to the active view or the composer.
|
||||
pub fn handle_key_event(
|
||||
&mut self,
|
||||
key_event: KeyEvent,
|
||||
) -> Result<InputResult, SendError<AppEvent>> {
|
||||
pub fn handle_key_event(&mut self, key_event: KeyEvent) -> InputResult {
|
||||
if let Some(mut view) = self.active_view.take() {
|
||||
view.handle_key_event(self, key_event)?;
|
||||
view.handle_key_event(self, key_event);
|
||||
if !view.is_complete() {
|
||||
self.active_view = Some(view);
|
||||
} else if self.is_task_running {
|
||||
@@ -70,31 +66,30 @@ impl BottomPane<'_> {
|
||||
height,
|
||||
)));
|
||||
}
|
||||
self.request_redraw()?;
|
||||
Ok(InputResult::None)
|
||||
self.request_redraw();
|
||||
InputResult::None
|
||||
} else {
|
||||
let (input_result, needs_redraw) = self.composer.handle_key_event(key_event);
|
||||
if needs_redraw {
|
||||
self.request_redraw()?;
|
||||
self.request_redraw();
|
||||
}
|
||||
Ok(input_result)
|
||||
input_result
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the status indicator text (only when the `StatusIndicatorView` is
|
||||
/// active).
|
||||
pub(crate) fn update_status_text(&mut self, text: String) -> Result<(), SendError<AppEvent>> {
|
||||
pub(crate) fn update_status_text(&mut self, text: String) {
|
||||
if let Some(view) = &mut self.active_view {
|
||||
match view.update_status_text(text) {
|
||||
ConditionalUpdate::NeedsRedraw => {
|
||||
self.request_redraw()?;
|
||||
self.request_redraw();
|
||||
}
|
||||
ConditionalUpdate::NoRedraw => {
|
||||
// No redraw needed.
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the UI to reflect whether this `BottomPane` has input focus.
|
||||
@@ -103,7 +98,7 @@ impl BottomPane<'_> {
|
||||
self.composer.set_input_focus(has_focus);
|
||||
}
|
||||
|
||||
pub fn set_task_running(&mut self, running: bool) -> Result<(), SendError<AppEvent>> {
|
||||
pub fn set_task_running(&mut self, running: bool) {
|
||||
self.is_task_running = running;
|
||||
|
||||
match (running, self.active_view.is_some()) {
|
||||
@@ -114,13 +109,13 @@ impl BottomPane<'_> {
|
||||
self.app_event_tx.clone(),
|
||||
height,
|
||||
)));
|
||||
self.request_redraw()?;
|
||||
self.request_redraw();
|
||||
}
|
||||
(false, true) => {
|
||||
if let Some(mut view) = self.active_view.take() {
|
||||
if view.should_hide_when_task_is_done() {
|
||||
// Leave self.active_view as None.
|
||||
self.request_redraw()?;
|
||||
self.request_redraw();
|
||||
} else {
|
||||
// Preserve the view.
|
||||
self.active_view = Some(view);
|
||||
@@ -131,20 +126,16 @@ impl BottomPane<'_> {
|
||||
// No change.
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called when the agent requests user approval.
|
||||
pub fn push_approval_request(
|
||||
&mut self,
|
||||
request: ApprovalRequest,
|
||||
) -> Result<(), SendError<AppEvent>> {
|
||||
pub fn push_approval_request(&mut self, request: ApprovalRequest) {
|
||||
let request = if let Some(view) = self.active_view.as_mut() {
|
||||
match view.try_consume_approval_request(request) {
|
||||
Some(request) => request,
|
||||
None => {
|
||||
self.request_redraw()?;
|
||||
return Ok(());
|
||||
self.request_redraw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -166,7 +157,7 @@ impl BottomPane<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn request_redraw(&self) -> Result<(), SendError<AppEvent>> {
|
||||
pub(crate) fn request_redraw(&self) {
|
||||
self.app_event_tx.send(AppEvent::Redraw)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
use std::sync::mpsc::SendError;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::widgets::WidgetRef;
|
||||
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::status_indicator_widget::StatusIndicatorWidget;
|
||||
|
||||
use super::BottomPane;
|
||||
use super::BottomPaneView;
|
||||
use super::bottom_pane_view::ConditionalUpdate;
|
||||
|
||||
@@ -18,7 +13,7 @@ pub(crate) struct StatusIndicatorView {
|
||||
}
|
||||
|
||||
impl StatusIndicatorView {
|
||||
pub fn new(app_event_tx: Sender<AppEvent>, height: u16) -> Self {
|
||||
pub fn new(app_event_tx: AppEventSender, height: u16) -> Self {
|
||||
Self {
|
||||
view: StatusIndicatorWidget::new(app_event_tx, height),
|
||||
}
|
||||
@@ -30,14 +25,6 @@ impl StatusIndicatorView {
|
||||
}
|
||||
|
||||
impl<'a> BottomPaneView<'a> for StatusIndicatorView {
|
||||
fn handle_key_event(
|
||||
&mut self,
|
||||
_pane: &mut BottomPane<'a>,
|
||||
_key_event: KeyEvent,
|
||||
) -> Result<(), SendError<AppEvent>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_status_text(&mut self, text: String) -> ConditionalUpdate {
|
||||
self.update_text(text);
|
||||
ConditionalUpdate::NeedsRedraw
|
||||
|
||||
Reference in New Issue
Block a user