use std::{
io::{stdout, Write},
time::Duration,
};
use atuin_common::utils::{self, Escapable as _};
use crossterm::{
cursor::SetCursorStyle,
event::{
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
KeyboardEnhancementFlags, MouseEvent, PopKeyboardEnhancementFlags,
PushKeyboardEnhancementFlags,
},
execute, terminal,
};
use eyre::Result;
use futures_util::FutureExt;
use semver::Version;
use unicode_width::UnicodeWidthStr;
use atuin_client::{
database::{current_context, Database},
history::{store::HistoryStore, History, HistoryStats},
settings::{CursorStyle, ExitMode, FilterMode, KeymapMode, SearchMode, Settings},
};
use super::{
cursor::Cursor,
engines::{SearchEngine, SearchState},
history_list::{HistoryList, ListState, PREFIX_LENGTH},
};
use crate::{command::client::search::engines, VERSION};
use ratatui::{
backend::CrosstermBackend,
layout::{Alignment, Constraint, Direction, Layout},
prelude::*,
style::{Color, Modifier, Style},
text::{Line, Span, Text},
widgets::{Block, BorderType, Borders, Paragraph, Tabs},
Frame, Terminal, TerminalOptions, Viewport,
};
const TAB_TITLES: [&str; 2] = ["Search", "Inspect"];
pub enum InputAction {
Accept(usize),
Copy(usize),
Delete(usize),
ReturnOriginal,
ReturnQuery,
Continue,
Redraw,
}
#[allow(clippy::struct_field_names)]
pub struct State {
history_count: i64,
update_needed: Option<Version>,
results_state: ListState,
switched_search_mode: bool,
search_mode: SearchMode,
results_len: usize,
accept: bool,
keymap_mode: KeymapMode,
current_cursor: Option<CursorStyle>,
tab_index: usize,
search: SearchState,
engine: Box<dyn SearchEngine>,
}
#[derive(Clone, Copy)]
struct StyleState {
compact: bool,
invert: bool,
inner_width: usize,
}
impl State {
async fn query_results(&mut self, db: &mut dyn Database) -> Result<Vec<History>> {
let results = self.engine.query(&self.search, db).await?;
self.results_state.select(0);
self.results_len = results.len();
Ok(results)
}
fn handle_input<W>(
&mut self,
settings: &Settings,
input: &Event,
w: &mut W,
) -> Result<InputAction>
where
W: Write,
{
execute!(w, EnableMouseCapture)?;
let r = match input {
Event::Key(k) => self.handle_key_input(settings, k),
Event::Mouse(m) => self.handle_mouse_input(*m),
Event::Paste(d) => self.handle_paste_input(d),
_ => InputAction::Continue,
};
execute!(w, DisableMouseCapture)?;
Ok(r)
}
fn handle_mouse_input(&mut self, input: MouseEvent) -> InputAction {
match input.kind {
eve