use super::{App, CiRequest, MouseGesture, Screen, hit_index, logs, page_step};
use crate::keymap::Action;
impl App {
pub(super) fn logs_mouse(&mut self, gesture: MouseGesture) {
let Some(view) = self.logs.as_mut() else {
return;
};
let last = view.rows().len().saturating_sub(1);
match gesture {
MouseGesture::Scroll { down, .. } => {
let delta = if down { 3 } else { -3 };
view.cursor = view.cursor.saturating_add_signed(delta).min(last);
}
MouseGesture::Press { col, row } => {
if let Some(i) = hit_index(view.body, view.scroll, col, row).filter(|i| *i <= last)
{
view.cursor = i;
view.visual_anchor = None;
}
}
MouseGesture::DoublePress { col, row } => {
if let Some(i) = hit_index(view.body, view.scroll, col, row).filter(|i| *i <= last)
{
view.cursor = i;
view.toggle_fold_at_cursor();
}
}
MouseGesture::Drag { col, row } => {
if let Some(i) = hit_index(view.body, view.scroll, col, row).filter(|i| *i <= last)
{
if view.visual_anchor.is_none() {
view.visual_anchor = Some(view.cursor);
}
view.cursor = i;
}
}
MouseGesture::Cancel => view.visual_anchor = None,
}
}
pub(super) fn open_runs(&mut self) {
if self.ci_remotes.is_empty() {
self.info("no CI provider detected for this repo");
return;
}
self.runs_cursor = 0;
self.push_screen(Screen::Runs);
self.pending_ci = Some(CiRequest::Runs);
}
pub(super) fn open_selected_run(&mut self) {
let Some(run) = self.runs.get(self.runs_cursor) else {
return;
};
let id = run.id.clone();
self.open_run_remote = run.remote.clone();
self.open_run = Some(id.clone());
self.extras = None;
self.graph = Some(crate::graph::GraphView::new());
self.push_screen(Screen::Graph);
self.pending_ci = Some(CiRequest::Detail(id));
}
pub(super) fn on_ci_log(
&mut self,
text: &str,
steps: Vec<crate::ci::LogStepMeta>,
offset: u64,
done: bool,
) {
self.log_text.push_str(text);
self.log_offset = offset;
self.log_done = done;
if !steps.is_empty() {
self.log_steps = steps;
}
let rebuilt = logs::LogsView::parse(&self.log_text, &self.log_steps);
self.logs = Some(match self.logs.take() {
Some(prev) => prev.carry_into(rebuilt),
None => rebuilt,
});
}
pub(super) fn on_ci_runs(&mut self, runs: Vec<crate::ci::CiRun>) {
self.runs = runs;
self.runs_cursor = self.runs_cursor.min(self.runs.len().saturating_sub(1));
self.clamp_cursor();
if !self.pr_checked {
self.pending_ci = Some(CiRequest::Pr);
}
}
pub(super) fn on_run_detail(&mut self, detail: &crate::ci::RunDetail) {
let model = crate::ci::to_model(detail);
if let Some(graph) = self.graph.as_mut() {
graph.set_model(model);
}
if self.extras.is_none()
&& self.screen() == Screen::Graph
&& let Some(run) = self.open_run.clone()
{
self.pending_ci = Some(CiRequest::Extras(run));
}
}
pub(super) fn open_logs(&mut self, job: crate::ci::JobId) {
let Some(run) = self.open_run.clone() else {
return;
};
self.open_job = Some(job.clone());
self.log_text.clear();
self.log_offset = 0;
self.log_steps.clear();
self.logs = None;
self.log_done = false;
self.push_screen(Screen::Logs);
self.pending_ci = Some(CiRequest::Log {
run,
job,
offset: 0,
});
}
pub(super) fn queue_ci_poll(&mut self) {
self.pending_ci = match self.screen() {
Screen::Status | Screen::Runs => Some(CiRequest::Runs),
Screen::Graph => self.open_run.clone().map(CiRequest::Detail),
Screen::Logs if self.log_done => None,
Screen::Logs => match (self.open_run.clone(), self.open_job.clone()) {
(Some(run), Some(job)) => Some(CiRequest::Log {
run,
job,
offset: self.log_offset,
}),
_ => None,
},
_ => None,
};
}
pub(super) fn dispatch_runs(&mut self, action: Action) {
let last = self.runs.len().saturating_sub(1);
match action {
Action::MoveDown => self.runs_cursor = (self.runs_cursor + 1).min(last),
Action::MoveUp => self.runs_cursor = self.runs_cursor.saturating_sub(1),
Action::GoTop => self.runs_cursor = 0,
Action::GoBottom => self.runs_cursor = last,
Action::HalfPageDown => {
self.runs_cursor = (self.runs_cursor + page_step(0, false)).min(last);
}
Action::HalfPageUp => {
self.runs_cursor = self.runs_cursor.saturating_sub(page_step(0, false));
}
Action::FullPageDown => {
self.runs_cursor = (self.runs_cursor + page_step(0, true)).min(last);
}
Action::FullPageUp => {
self.runs_cursor = self.runs_cursor.saturating_sub(page_step(0, true));
}
Action::Open => self.open_selected_run(),
_ => {}
}
}
pub(super) fn dispatch_logs(&mut self, action: Action) {
let Some(view) = self.logs.as_mut() else {
return;
};
let last = view.rows().len().saturating_sub(1);
match action {
Action::MoveDown => view.cursor = (view.cursor + 1).min(last),
Action::MoveUp => view.cursor = view.cursor.saturating_sub(1),
Action::GoTop => view.cursor = 0,
Action::GoBottom => view.cursor = last,
Action::HalfPageDown => self.logs_page(false, false),
Action::HalfPageUp => self.logs_page(true, false),
Action::FullPageDown => self.logs_page(false, true),
Action::FullPageUp => self.logs_page(true, true),
Action::ToggleFold => view.toggle_fold_at_cursor(),
Action::VisualSelect => {
view.visual_anchor = match view.visual_anchor {
Some(_) => None,
None => Some(view.cursor),
};
}
Action::CopyFileFeedback | Action::CopyAllFeedback => {
self.pending_clipboard = Some(view.selection_text());
let view = self.logs.as_mut();
if let Some(view) = view {
view.visual_anchor = None;
}
self.info("yanked log selection");
}
_ => {}
}
}
pub(super) fn logs_page(&mut self, up: bool, full: bool) {
let Some(view) = self.logs.as_mut() else {
return;
};
let last = view.rows().len().saturating_sub(1);
let step = page_step(view.viewport, full);
view.cursor = if up {
view.cursor.saturating_sub(step)
} else {
(view.cursor + step).min(last)
};
}
pub(super) fn dispatch_graph(&mut self, action: Action) {
use crate::graph::Dir;
let Some(graph) = self.graph.as_mut() else {
return;
};
match action {
Action::MoveDown => graph.move_selection(Dir::Down),
Action::MoveUp => graph.move_selection(Dir::Up),
Action::MoveLeft => graph.move_selection(Dir::Left),
Action::MoveRight => graph.move_selection(Dir::Right),
Action::GoTop => graph.select_end(true),
Action::GoBottom => graph.select_end(false),
Action::ZoomIn => {
let zoom = graph.zoom().in_();
graph.set_zoom(zoom);
}
Action::ZoomOut => {
let zoom = graph.zoom().out();
graph.set_zoom(zoom);
}
Action::Open => {
if let Some(action) = graph.activate() {
self.on_graph_action(&action);
}
}
_ => {}
}
if self.search.is_some() {
let rows = self.focused_search_rows();
if let Some(search) = self.search.as_mut() {
search.recompute(&rows);
}
}
}
pub(super) fn on_graph_action(&mut self, action: &crate::graph::GraphAction) {
match action {
crate::graph::GraphAction::Activated(id) => {
if let Some((path, line)) = self.impact_targets.get(&id.0).cloned() {
self.request_editor(&path, Some(line + 1));
} else if self.impact_title.is_none() {
self.open_logs(crate::ci::JobId(id.0.clone()));
}
}
crate::graph::GraphAction::Folded { .. } => {}
}
}
}