use super::{App, Screen};
pub trait RowSelect {
fn cursor(&self) -> usize;
fn anchor(&self) -> Option<usize>;
fn set_anchor(&mut self, anchor: Option<usize>);
fn selection(&self) -> Option<(usize, usize)> {
let anchor = self.anchor()?;
Some((anchor.min(self.cursor()), anchor.max(self.cursor())))
}
fn row_selected(&self, row: usize) -> bool {
row == self.cursor()
|| self
.selection()
.is_some_and(|(lo, hi)| row >= lo && row <= hi)
}
fn toggle_visual(&mut self) {
let next = match self.anchor() {
Some(_) => None,
None => Some(self.cursor()),
};
self.set_anchor(next);
}
fn clamp_anchor(&mut self, last: usize) {
let clamped = self.anchor().map(|anchor| anchor.min(last));
self.set_anchor(clamped);
}
}
pub trait RowText: RowSelect {
fn row_count(&self) -> usize;
fn row_text(&self, row: usize) -> String;
fn selection_text(&self) -> String {
let last = self.row_count().saturating_sub(1);
let (lo, hi) = self.selection().unwrap_or((self.cursor(), self.cursor()));
(lo.min(last)..=hi.min(last))
.map(|row| self.row_text(row))
.collect::<Vec<_>>()
.join("\n")
}
}
impl App {
pub(crate) fn row_select_mut(&mut self) -> Option<&mut dyn RowSelect> {
match self.screen() {
Screen::Diff => self.diff.as_mut().map(|view| view as &mut dyn RowSelect),
Screen::Log => self.log.as_mut().map(|view| view as &mut dyn RowSelect),
Screen::CiLog => self.ci_log.as_mut().map(|view| view as &mut dyn RowSelect),
Screen::File => self.file.as_mut().map(|view| view as &mut dyn RowSelect),
Screen::Status => Some(&mut self.status),
Screen::Graph | Screen::Runs | Screen::Prs | Screen::Stats => None,
}
}
pub(crate) fn visual_active(&self) -> bool {
match self.screen() {
Screen::Diff => self.diff.as_ref().is_some_and(|v| v.anchor().is_some()),
Screen::Log => self.log.as_ref().is_some_and(|v| v.anchor().is_some()),
Screen::CiLog => self.ci_log.as_ref().is_some_and(|v| v.anchor().is_some()),
Screen::File => self.file.as_ref().is_some_and(|v| v.anchor().is_some()),
Screen::Status => self.status.anchor().is_some(),
Screen::Graph | Screen::Runs | Screen::Prs | Screen::Stats => false,
}
}
fn row_text_view(&self) -> Option<&dyn RowText> {
match self.screen() {
Screen::Diff => self.diff.as_ref().map(|view| view as &dyn RowText),
Screen::Log => self.log.as_ref().map(|view| view as &dyn RowText),
Screen::CiLog => self.ci_log.as_ref().map(|view| view as &dyn RowText),
Screen::File => self.file.as_ref().map(|view| view as &dyn RowText),
Screen::Status | Screen::Graph | Screen::Runs | Screen::Prs | Screen::Stats => None,
}
}
pub(crate) fn yank_rows(&mut self, note: &str) {
let Some(text) = self.row_text_view().map(RowText::selection_text) else {
return;
};
self.pending_clipboard = Some(text);
if let Some(view) = self.row_select_mut() {
view.set_anchor(None);
}
self.info(note.to_owned());
}
}