#![allow(unused_imports)]
use crate::error::AlternateScreenPagingError;
#[cfg(feature = "search")]
use crate::utils::SearchMode;
use crate::Pager;
use crossterm::{
cursor::{self, MoveTo},
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
style::Attribute,
terminal::{Clear, ClearType},
};
use std::time::Duration;
#[cfg(all(feature = "static_output", feature = "search"))]
pub(crate) fn fetch_input_blocking(
out: &mut impl std::io::Write,
search_mode: crate::utils::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: crate::utils::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,
query: &str,
) -> Result<Vec<(u16, u16)>, regex::Error> {
let pattern = regex::Regex::new(query)?;
let mut coordinates: Vec<(u16, u16)> = Vec::new();
pager.search_lines = pager.lines.clone();
let mut lines: Vec<String> = pager
.search_lines
.lines()
.map(std::string::ToString::to_string)
.collect();
for (i, line) in lines.iter_mut().enumerate() {
if let Some(cap) = pattern.captures(&line.clone()) {
let text = format!("{}{}{}", Attribute::Reverse, &cap[0], Attribute::Reset);
let text = text.as_str();
let replace = pattern.replace_all(line, text).to_string();
find(line.clone(), &cap[0]).iter().for_each(|x| {
#[allow(clippy::cast_possible_truncation)]
coordinates.push((*x as u16, i as u16));
});
*line = replace;
}
}
pager.search_lines = lines.join("\n");
pager.search_lines.push('\n');
Ok(coordinates)
}
#[cfg(feature = "search")]
pub(crate) fn find(mut text: String, query: &str) -> Vec<usize> {
let mut points: Vec<usize> = Vec::new();
let mut searched = 0;
text = text.replace('\t', " ");
while let Some(x) = text.find(&query) {
points.push(searched + x);
let truncate = x + query.char_indices().count();
text.drain(..truncate);
searched += truncate;
}
points
}