use ratatui::{
buffer::Buffer,
layout::Rect,
style::{Modifier, Style},
widgets::{Block, Widget},
};
use super::super::{
nu_common::{NuStyle, string_width},
views::util::{nu_style_to_tui, set_span},
};
#[derive(Debug)]
pub struct CommandBar<'a> {
text: &'a str,
information: &'a str,
text_s: Style,
back_s: Style,
}
impl<'a> CommandBar<'a> {
pub fn new(text: &'a str, information: &'a str, text_s: NuStyle, back_s: NuStyle) -> Self {
let text_s = nu_style_to_tui(text_s).add_modifier(Modifier::BOLD);
let back_s = nu_style_to_tui(back_s);
Self {
text,
information,
text_s,
back_s,
}
}
}
impl Widget for CommandBar<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
const INFO_PADDING_RIGHT: u16 = 2;
const TEXT_PADDING_LEFT: u16 = 1;
let block = Block::default().style(self.back_s);
block.render(area, buf);
let text_x = area.x + TEXT_PADDING_LEFT;
let info_width = string_width(self.information) as u16;
let max_text_width = area
.width
.saturating_sub(TEXT_PADDING_LEFT + info_width + INFO_PADDING_RIGHT + 2);
let text_width = set_span(
buf,
(text_x, area.y),
self.text,
self.text_s,
max_text_width,
);
let available_width = area.width.saturating_sub(text_x - area.x + text_width);
if available_width <= INFO_PADDING_RIGHT + 2 || self.information.is_empty() {
return;
}
let info_x = area.right().saturating_sub(info_width + INFO_PADDING_RIGHT);
if info_x > text_x + text_width + 1 {
set_span(
buf,
(info_x, area.y),
self.information,
self.text_s,
info_width,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn buffer_to_string(buf: &Buffer) -> String {
let mut result = String::new();
for y in 0..buf.area.height {
for x in 0..buf.area.width {
if let Some(cell) = buf.cell((x, y)) {
result.push_str(cell.symbol());
}
}
if y < buf.area.height - 1 {
result.push('\n');
}
}
result
}
fn render_command_bar(text: &str, information: &str, width: u16, height: u16) -> Buffer {
let area = Rect::new(0, 0, width, height);
let mut buf = Buffer::empty(area);
let cmd_bar = CommandBar::new(text, information, NuStyle::default(), NuStyle::default());
cmd_bar.render(area, &mut buf);
buf
}
#[test]
fn test_command_bar_basic_text() {
let buf = render_command_bar(":help", "", 40, 1);
let content = buffer_to_string(&buf);
assert!(content.contains(":help"));
}
#[test]
fn test_command_bar_with_information() {
let buf = render_command_bar(":search", "[1/5]", 40, 1);
let content = buffer_to_string(&buf);
assert!(content.contains(":search"));
assert!(content.contains("[1/5]"));
}
#[test]
fn test_command_bar_text_on_left_info_on_right() {
let buf = render_command_bar("/pattern", "[3/10]", 50, 1);
let content = buffer_to_string(&buf);
let text_pos = content.find("/pattern").unwrap();
let info_pos = content.find("[3/10]").unwrap();
assert!(text_pos < info_pos);
}
#[test]
fn test_command_bar_empty_text() {
let buf = render_command_bar("", "[info]", 40, 1);
let content = buffer_to_string(&buf);
assert!(content.contains("[info]"));
}
#[test]
fn test_command_bar_empty_information() {
let buf = render_command_bar(":quit", "", 40, 1);
let content = buffer_to_string(&buf);
assert!(content.contains(":quit"));
}
#[test]
fn test_command_bar_both_empty() {
let buf = render_command_bar("", "", 40, 1);
assert_eq!(buf.area.width, 40);
}
#[test]
fn test_command_bar_narrow_width() {
let buf = render_command_bar(":x", "[info]", 10, 1);
assert_eq!(buf.area.width, 10);
}
#[test]
fn test_command_bar_unicode_text() {
let buf = render_command_bar("/日本語", "[結果]", 50, 1);
let content = buffer_to_string(&buf);
assert!(content.contains("日本語") || !content.is_empty());
}
#[test]
fn test_command_bar_left_padding() {
let buf = render_command_bar(":a", "", 20, 1);
let content = buffer_to_string(&buf);
let first_char = content.chars().next().unwrap();
assert_eq!(first_char, ' ');
}
#[test]
fn test_command_bar_zero_height() {
let area = Rect::new(0, 0, 40, 0);
assert_eq!(area.height, 0);
}
#[test]
fn test_command_bar_zero_width() {
let buf = render_command_bar(":test", "[info]", 0, 1);
assert_eq!(buf.area.width, 0);
}
#[test]
fn test_command_bar_long_text_and_info() {
let long_text = ":".to_string() + &"a".repeat(50);
let long_info = "b".repeat(20);
let buf = render_command_bar(&long_text, &long_info, 40, 1);
assert_eq!(buf.area.width, 40);
}
#[test]
fn test_command_bar_search_pattern() {
let buf = render_command_bar("/search_pattern", "[0/0]", 40, 1);
let content = buffer_to_string(&buf);
assert!(content.contains("/search_pattern"));
assert!(content.contains("[0/0]"));
}
#[test]
fn test_command_bar_reverse_search() {
let buf = render_command_bar("?reverse", "[2/5]", 40, 1);
let content = buffer_to_string(&buf);
assert!(content.contains("?reverse"));
}
}