use crate::theme::Theme;
use crate::util::text::wrap_text;
use ratatui::{
style::Style,
text::{Line, Span},
};
pub fn cursor_spans<'a>(value: &str, cursor: usize, style: Style, theme: &Theme) -> Vec<Span<'a>> {
let chars: Vec<char> = value.chars().collect();
let before: String = chars[..cursor.min(chars.len())].iter().collect();
let cursor_ch = if cursor < chars.len() {
chars[cursor].to_string()
} else {
" ".to_string()
};
let after: String = if cursor < chars.len() {
chars[cursor + 1..].iter().collect()
} else {
String::new()
};
vec![
Span::styled(before, style),
Span::styled(
cursor_ch,
Style::default().fg(theme.cursor_fg).bg(theme.cursor_bg),
),
Span::styled(after, style),
Span::styled(" \u{270f}\u{fe0f}", Style::default()),
]
}
#[derive(Debug)]
pub struct WrappedCursorLines {
pub lines: Vec<Line<'static>>,
}
pub fn cursor_wrapped_lines(
input: &str,
cursor_pos: usize,
width: usize,
placeholder: Option<&str>,
theme: &Theme,
) -> WrappedCursorLines {
let cursor_style = Style::default().fg(theme.cursor_fg).bg(theme.cursor_bg);
let text_style = Style::default().fg(theme.text_normal);
let placeholder_style = Style::default().fg(theme.text_dim);
if input.is_empty() {
let placeholder_text = placeholder.unwrap_or("输入内容…");
return WrappedCursorLines {
lines: vec![Line::from(vec![
Span::styled(" ", cursor_style),
Span::styled(format!(" {}", placeholder_text), placeholder_style),
])],
};
}
let wrapped = wrap_text(input, width);
let mut char_offset = 0;
let mut result = Vec::with_capacity(wrapped.len());
let mut cursor_placed = false;
for (line_idx, line_str) in wrapped.iter().enumerate() {
let line_chars: Vec<char> = line_str.chars().collect();
let line_len = line_chars.len();
let is_last = line_idx == wrapped.len() - 1;
let cursor_on_this_line = !cursor_placed
&& (cursor_pos < char_offset + line_len
|| (is_last && cursor_pos == char_offset + line_len));
if cursor_on_this_line {
cursor_placed = true;
let pos_in_line = cursor_pos - char_offset;
let before: String = line_chars[..pos_in_line].iter().collect();
let (cursor_ch, after) = if pos_in_line < line_len {
(
line_chars[pos_in_line].to_string(),
line_chars[pos_in_line + 1..].iter().collect::<String>(),
)
} else {
(" ".to_string(), String::new())
};
result.push(Line::from(vec![
Span::styled(before, text_style),
Span::styled(cursor_ch, cursor_style),
Span::styled(after, text_style),
]));
} else {
result.push(Line::from(Span::styled(line_str.clone(), text_style)));
}
char_offset += line_len;
}
WrappedCursorLines { lines: result }
}