use hjkl_engine::{Input as EngineInput, Key as EngineKey, VimMode};
use hjkl_form::TextFieldEditor;
use super::{App, SearchDir};
impl App {
pub(crate) fn open_command_prompt(&mut self) {
let mut field = TextFieldEditor::new(true);
field.enter_insert_at_end();
self.command_field = Some(field);
}
pub(crate) fn open_command_prompt_with(&mut self, prefill: &str) {
let mut field = TextFieldEditor::new(true);
field.enter_insert_at_end();
for c in prefill.chars() {
let input = EngineInput {
key: EngineKey::Char(c),
ctrl: false,
alt: false,
shift: false,
};
field.handle_input(input);
}
self.command_field = Some(field);
}
pub(crate) fn handle_command_field_key(&mut self, key: crossterm::event::KeyEvent) {
let input: EngineInput = key.into();
let field = match self.command_field.as_mut() {
Some(f) => f,
None => return,
};
if input.key == EngineKey::Enter {
let text = field.text();
self.command_field = None;
self.dispatch_ex(text.trim());
return;
}
if input.key == EngineKey::Esc {
if field.text().is_empty() {
self.command_field = None;
} else if field.vim_mode() == VimMode::Insert {
field.enter_normal();
} else {
self.command_field = None;
}
return;
}
field.handle_input(input);
}
pub(crate) fn open_search_prompt(&mut self, dir: SearchDir) {
let mut field = TextFieldEditor::new(true);
field.enter_insert_at_end();
self.search_field = Some(field);
self.search_dir = dir;
self.active_mut().editor.set_search_pattern(None);
}
pub(crate) fn cancel_search_prompt(&mut self) {
self.search_field = None;
let last = self.active().editor.last_search().map(str::to_owned);
match last {
Some(p) if !p.is_empty() => {
if let Ok(re) = regex::Regex::new(&p) {
self.active_mut().editor.set_search_pattern(Some(re));
} else {
self.active_mut().editor.set_search_pattern(None);
}
}
_ => self.active_mut().editor.set_search_pattern(None),
}
}
pub(crate) fn handle_search_field_key(&mut self, key: crossterm::event::KeyEvent) {
let input: EngineInput = key.into();
let field = match self.search_field.as_mut() {
Some(f) => f,
None => return,
};
if input.key == EngineKey::Enter {
let pattern = field.text();
self.search_field = None;
self.commit_search(&pattern);
return;
}
if input.key == EngineKey::Esc {
if field.text().is_empty() {
self.cancel_search_prompt();
return;
}
if field.vim_mode() == VimMode::Insert {
field.enter_normal();
} else {
self.cancel_search_prompt();
}
return;
}
let dirty = field.handle_input(input);
if dirty {
self.live_preview_search();
}
}
pub(crate) fn live_preview_search(&mut self) {
let pattern = match self.search_field.as_ref() {
Some(f) => f.text(),
None => return,
};
if pattern.is_empty() {
self.active_mut().editor.set_search_pattern(None);
return;
}
let case_insensitive = self.active().editor.settings().ignore_case
&& !(self.active().editor.settings().smartcase
&& pattern.chars().any(|c| c.is_uppercase()));
let effective: std::borrow::Cow<'_, str> = if case_insensitive {
std::borrow::Cow::Owned(format!("(?i){pattern}"))
} else {
std::borrow::Cow::Borrowed(pattern.as_str())
};
match regex::Regex::new(&effective) {
Ok(re) => self.active_mut().editor.set_search_pattern(Some(re)),
Err(_) => self.active_mut().editor.set_search_pattern(None),
}
}
pub(crate) fn commit_search(&mut self, pattern: &str) {
let effective: Option<String> = if pattern.is_empty() {
self.active().editor.last_search().map(str::to_owned)
} else {
Some(pattern.to_owned())
};
let Some(p) = effective else {
self.active_mut().editor.set_search_pattern(None);
return;
};
let case_insensitive = self.active().editor.settings().ignore_case
&& !(self.active().editor.settings().smartcase && p.chars().any(|c| c.is_uppercase()));
let compile_src: std::borrow::Cow<'_, str> = if case_insensitive {
std::borrow::Cow::Owned(format!("(?i){p}"))
} else {
std::borrow::Cow::Borrowed(p.as_str())
};
match regex::Regex::new(&compile_src) {
Ok(re) => {
self.active_mut().editor.set_search_pattern(Some(re));
let forward = self.search_dir == SearchDir::Forward;
if forward {
self.active_mut().editor.search_advance_forward(false);
} else {
self.active_mut().editor.search_advance_backward(true);
}
self.active_mut().editor.ensure_cursor_in_scrolloff();
self.sync_viewport_from_editor();
self.active_mut().editor.set_last_search(Some(p), forward);
}
Err(e) => {
self.active_mut().editor.set_search_pattern(None);
self.status_message = Some(format!("E: bad search pattern: {e}"));
}
}
}
}