#![allow(unused_imports)]
use crate::error::AlternateScreenPagingError;
use crate::Pager;
use crossterm::{
cursor::{self, MoveTo},
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
style::Attribute,
terminal::{Clear, ClearType},
};
use std::{convert::TryFrom, time::Duration};
#[derive(PartialEq, Clone, Copy, Debug)]
#[cfg(feature = "search")]
pub enum SearchMode {
Forward,
Reverse,
Unknown,
}
#[cfg(all(feature = "static_output", feature = "search"))]
pub(crate) fn fetch_input_blocking(
out: &mut impl std::io::Write,
search_mode: SearchMode,
rows: usize,
) -> Result<String, AlternateScreenPagingError> {
#[allow(clippy::cast_possible_truncation)]
write!(
out,
"{}{}{}{}",
MoveTo(0, rows as u16),
Clear(ClearType::CurrentLine),
if search_mode == SearchMode::Forward {
"/"
} else {
"?"
},
cursor::Show
)?;
out.flush()?;
let mut string = String::new();
loop {
if event::poll(Duration::from_millis(10))
.map_err(|e| AlternateScreenPagingError::HandleEvent(e.into()))?
{
match event::read().map_err(|e| AlternateScreenPagingError::HandleEvent(e.into()))? {
Event::Key(KeyEvent {
code: KeyCode::Esc,
modifiers: KeyModifiers::NONE,
}) => {
return Ok(String::new());
}
Event::Key(KeyEvent {
code: KeyCode::Backspace,
modifiers: KeyModifiers::NONE,
}) => {
string.pop();
write!(out, "\r{}/{}", Clear(ClearType::CurrentLine), string)?;
out.flush()?;
}
Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
}) => {
return Ok(string);
}
Event::Key(event) => {
if let KeyCode::Char(c) = event.code {
string.push(c);
write!(out, "\r/{}", string)?;
out.flush()?;
}
}
_ => continue,
}
}
}
}
#[cfg(all(
any(feature = "async_std_lib", feature = "tokio_lib"),
feature = "search"
))]
pub(crate) async fn fetch_input(
out: &mut impl std::io::Write,
search_mode: SearchMode,
rows: usize,
) -> Result<String, AlternateScreenPagingError> {
#[allow(clippy::cast_possible_truncation)]
write!(
out,
"{}{}{}{}",
MoveTo(0, rows as u16),
Clear(ClearType::CurrentLine),
if search_mode == SearchMode::Forward {
"/"
} else {
"?"
},
cursor::Show
)?;
out.flush()?;
let mut string = String::new();
loop {
if event::poll(Duration::from_millis(10))
.map_err(|e| AlternateScreenPagingError::HandleEvent(e.into()))?
{
match event::read().map_err(|e| AlternateScreenPagingError::HandleEvent(e.into()))? {
Event::Key(KeyEvent {
code: KeyCode::Esc,
modifiers: KeyModifiers::NONE,
}) => {
return Ok(String::new());
}
Event::Key(KeyEvent {
code: KeyCode::Backspace,
modifiers: KeyModifiers::NONE,
}) => {
string.pop();
write!(out, "\r{}/{}", Clear(ClearType::CurrentLine), string)?;
out.flush()?;
}
Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
}) => {
return Ok(string);
}
Event::Key(event) => {
if let KeyCode::Char(c) = event.code {
string.push(c);
write!(out, "\r/{}", string)?;
out.flush()?;
}
}
_ => continue,
}
}
}
}
#[cfg(feature = "search")]
pub(crate) fn highlight_search(pager: &mut Pager) {
let pattern = pager.search_term.as_ref().unwrap();
let mut coordinates: Vec<u16> = Vec::new();
for (idx, line) in pager.get_flattened_lines().enumerate() {
if pattern.is_match(&(*line).to_string()) {
coordinates.push(u16::try_from(idx).unwrap())
}
}
pager.search_idx = coordinates;
}
#[cfg(feature = "search")]
pub(crate) fn highlight_line_matches(line: &mut String, query: ®ex::Regex) {
if let Some(cap) = query.captures(line) {
let text = format!("{}{}{}", Attribute::Reverse, &cap[0], Attribute::Reset);
let text = text.as_str();
*line = query.replace_all(&line, text).to_string();
}
}