use super::diagnostics;
use super::{Action, App, Component, KeyEvent, Mode, Outcome, keymap};
impl App {
pub fn handle_event(&mut self, event: crate::event::AppEvent) {
use crate::event::AppEvent;
use ratatui::crossterm::event::Event;
match event {
AppEvent::Terminal(Event::Key(key)) => self.handle_key(key),
AppEvent::Terminal(Event::Resize(columns, rows)) => {
rootle_trace::record_with(
rootle_trace::EventKind::Resize,
|| serde_json::json!({"columns": columns, "rows": rows, "source": "input"}),
);
}
AppEvent::Terminal(ignored) => {
rootle_trace::record_with(rootle_trace::EventKind::Input, || match ignored {
Event::Paste(text) => serde_json::json!({
"kind": "paste", "handled": false, "bytes": text.len(),
"text": rootle_trace::capture_content().then_some(text.as_str()),
}),
Event::FocusGained => {
serde_json::json!({"kind": "focus_gained", "handled": false})
}
Event::FocusLost => {
serde_json::json!({"kind": "focus_lost", "handled": false})
}
Event::Mouse(_) => serde_json::json!({"kind": "mouse", "handled": false}),
Event::Key(_) | Event::Resize(..) => unreachable!(),
});
}
worker => self.handle_app_event(worker),
}
}
pub(super) fn effective_mode(&self) -> Mode {
if self.consent.is_some() {
return Mode::Browse;
}
if let Some(wizard) = &self.wizard {
return wizard.effective_mode();
}
if let Some(settings) = &self.settings {
return settings.effective_mode();
}
if let Some(help) = &self.help {
return help.effective_mode();
}
if let Some(refs) = &self.refs_popup {
return refs.effective_mode();
}
if self.command_line.is_some() {
return Mode::Insert;
}
if self.mode == Mode::History && self.browser.history_filtering() {
return Mode::Search;
}
if self.mode == Mode::Commit
&& self
.browser
.commit_ref()
.is_some_and(|view| view.filtering())
{
return Mode::Search;
}
if self.mode == Mode::Commit
&& self
.browser
.commit_ref()
.is_some_and(|view| view.searching())
{
return Mode::Find;
}
if let Some(view) = &self.search_view {
if self.mode == Mode::Leader {
return Mode::Leader;
}
return view.effective_mode();
}
match &self.popup {
Some(popup) => popup.effective_mode(),
None => self.mode,
}
}
pub fn handle_key(&mut self, key: KeyEvent) {
rootle_trace::in_operation(rootle_trace::operation_id(), || {
diagnostics::record_key(&key, self.effective_mode().chip());
let action = self.dispatch(key);
self.handle_action(action);
diagnostics::record_state(self, "post_key");
});
}
pub(super) fn dispatch(&mut self, key: KeyEvent) -> Action {
if let Some(consent) = &mut self.consent {
return consent.handle_key(key);
}
if let Some(wizard) = &mut self.wizard {
return wizard.handle_key(key);
}
if let Some(settings) = &mut self.settings {
return settings.handle_key(key);
}
if let Some(help) = &mut self.help {
return help.handle_key(key);
}
if let Some(refs) = &mut self.refs_popup {
return refs.handle_key(key);
}
if let Some(command_line) = &mut self.command_line {
return command_line.handle_key(key);
}
if let Some(view) = &mut self.search_view {
if self.mode == Mode::Leader {
return keymap::leader(key.code);
}
return view.handle_key(key);
}
if let Some(popup) = &mut self.popup {
return popup.handle_key(key);
}
match self.mode {
Mode::Browse => keymap::browsing(key.code),
Mode::Visual => keymap::visual(key.code),
Mode::Search => match self.browser.filter_input.handle_key(key) {
Outcome::Changed => Action::Noop, Outcome::Submitted => Action::CommitFilter,
Outcome::Cancelled => Action::ClearFilter,
Outcome::Noop => Action::Noop,
},
Mode::Find => match self.browser.find_input.handle_key(key) {
Outcome::Changed => Action::UpdateFind,
Outcome::Submitted => Action::CommitFind,
Outcome::Cancelled => Action::CancelFind,
Outcome::Noop => Action::Noop,
},
Mode::Leader => keymap::leader(key.code),
Mode::History => {
if self.browser.history_filtering() {
Action::HistoryFilterKey(key)
} else if self.browser.repository_history_active() {
keymap::repository_history(key.code)
} else {
keymap::history(key.code)
}
}
Mode::Preview => self.browser.preview_key(key),
Mode::Commit => self
.browser
.commit()
.map(|view| view.handle_key(key))
.unwrap_or(Action::Noop),
_ => Action::Noop,
}
}
pub fn handle_action(&mut self, action: Action) {
diagnostics::record_action(&action);
match &action {
Action::RepoSelected { .. } | Action::OrgSelected(_) | Action::LoadRepoTree { .. } => {
self.requests.search = crate::request::LoadState::default();
self.requests.invalidate_browser();
}
Action::BlameToggle => self.requests.blame.invalidate(),
Action::HistoryUp | Action::HistoryDown => self.requests.historical_blob.invalidate(),
Action::ClosePopup => {
self.requests.search = crate::request::LoadState::default();
self.requests.references.invalidate();
}
_ => {}
}
let left = self
.try_browse(action)
.and_then(|a| self.try_search(a))
.and_then(|a| self.try_lenses(a))
.and_then(|a| self.try_lifecycle(a));
debug_assert!(left.is_none(), "unrouted action: {left:?}");
let _ = left;
if !self.browser.history_is_open() {
self.requests.historical_blob.invalidate();
}
if self.mode == Mode::Search {
self.browser.apply_filter();
}
self.sync_highlight_theme();
self.maybe_load_blob();
if let Some(note) = self.provider.take_notice() {
if note.contains("keeps failing to restart") {
self.degraded = Some(note.clone());
}
self.status = Some(note);
}
diagnostics::record_state(self, "post_action");
}
}