use crate::vim::{
LeaderState, SearchDirection, VimCommandState, VimConfig, VimSearchState, VimStatusLine,
VimStatusMessage,
};
pub(super) fn render_search_status(
search_state: &VimSearchState,
command_state: &VimCommandState,
leader_state: &LeaderState,
vim_config: &VimConfig,
status_line: &VimStatusLine,
) -> RenderedStatusLine {
if let Some(command) = command_state.active_command() {
return RenderedStatusLine::prompt(format!(":{}", command.as_str()));
}
if let Some(query) = search_state.active_query() {
let prompt = match search_state.active_direction() {
Some(SearchDirection::Backward) => '?',
Some(SearchDirection::Forward) | None => '/',
};
return RenderedStatusLine::prompt(format!("{prompt}{}", query.as_str()));
}
if leader_state.is_menu_visible() {
let labels = vim_config
.leader
.bindings
.iter()
.map(|binding| binding.label.as_str())
.collect::<Vec<_>>()
.join(" ");
return RenderedStatusLine::prompt(format!("<leader> {labels}"));
}
match status_line.message() {
Some(VimStatusMessage::Error(error)) => RenderedStatusLine::error(error.status_text()),
Some(VimStatusMessage::Info(message)) => RenderedStatusLine::prompt(message.clone()),
None => RenderedStatusLine::prompt(String::new()),
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct RenderedStatusLine {
pub(super) text: String,
pub(super) kind: StatusLineKind,
}
impl RenderedStatusLine {
const fn prompt(text: String) -> Self {
Self {
text,
kind: StatusLineKind::Prompt,
}
}
const fn error(text: String) -> Self {
Self {
text,
kind: StatusLineKind::Error,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum StatusLineKind {
Prompt,
Error,
}
#[cfg(test)]
mod tests {
use super::{RenderedStatusLine, render_search_status};
use crate::vim::{
LeaderState, SearchDirection, VimCommandState, VimConfig, VimError, VimSearchState,
VimStatusLine,
};
#[test]
fn search_status_renders_active_prompt_and_terminal_outcomes() {
let mut search_state = VimSearchState::default();
let command_state = VimCommandState::default();
let leader_state = LeaderState::default();
let vim_config = VimConfig::default();
let mut status_line = VimStatusLine::default();
assert_eq!(
render_search_status(
&search_state,
&command_state,
&leader_state,
&vim_config,
&status_line
),
RenderedStatusLine::prompt(String::new())
);
search_state.start(SearchDirection::Forward);
search_state.push_text("λ");
assert_eq!(
render_search_status(
&search_state,
&command_state,
&leader_state,
&vim_config,
&status_line
),
RenderedStatusLine::prompt(String::from("/λ"))
);
let _outcome = search_state.submit("abc", 0);
status_line.set_error(VimError::PatternNotFound);
assert_eq!(
render_search_status(
&search_state,
&command_state,
&leader_state,
&vim_config,
&status_line
),
RenderedStatusLine::error(String::from("E486: Pattern not found"))
);
}
#[test]
fn command_status_takes_precedence() {
let search_state = VimSearchState::default();
let mut command_state = VimCommandState::default();
let leader_state = LeaderState::default();
let vim_config = VimConfig::default();
let status_line = VimStatusLine::default();
command_state.start();
command_state.push_text("wq");
assert_eq!(
render_search_status(
&search_state,
&command_state,
&leader_state,
&vim_config,
&status_line
),
RenderedStatusLine::prompt(String::from(":wq"))
);
}
#[test]
fn leader_menu_renders_from_configured_bindings() {
let search_state = VimSearchState::default();
let command_state = VimCommandState::default();
let mut leader_state = LeaderState::default();
let vim_config = VimConfig::default();
let status_line = VimStatusLine::default();
leader_state.start();
leader_state.tick(vim_config.leader.delay_ms, vim_config.leader.delay_ms);
assert_eq!(
render_search_status(
&search_state,
&command_state,
&leader_state,
&vim_config,
&status_line
),
RenderedStatusLine::prompt(String::from(
"<leader> w write q quit x write-quit n next search N previous search f page down b page up h help"
))
);
}
}