use crate::key::{self, KeyPress};
use crossterm::event::{KeyCode, KeyModifiers};
use lipgloss_extras::prelude::*;
#[derive(Debug, Clone)]
pub struct TextareaKeyMap {
pub character_backward: key::Binding,
pub character_forward: key::Binding,
pub delete_after_cursor: key::Binding,
pub delete_before_cursor: key::Binding,
pub delete_character_backward: key::Binding,
pub delete_character_forward: key::Binding,
pub delete_word_backward: key::Binding,
pub delete_word_forward: key::Binding,
pub insert_newline: key::Binding,
pub line_end: key::Binding,
pub line_next: key::Binding,
pub line_previous: key::Binding,
pub line_start: key::Binding,
pub paste: key::Binding,
pub word_backward: key::Binding,
pub word_forward: key::Binding,
pub input_begin: key::Binding,
pub input_end: key::Binding,
pub uppercase_word_forward: key::Binding,
pub lowercase_word_forward: key::Binding,
pub capitalize_word_forward: key::Binding,
pub transpose_character_backward: key::Binding,
}
impl crate::key::KeyMap for TextareaKeyMap {
fn short_help(&self) -> Vec<&key::Binding> {
vec![
&self.character_backward,
&self.character_forward,
&self.line_next,
&self.line_previous,
&self.insert_newline,
&self.delete_character_backward,
]
}
fn full_help(&self) -> Vec<Vec<&key::Binding>> {
vec![
vec![
&self.character_backward,
&self.character_forward,
&self.word_backward,
&self.word_forward,
],
vec![
&self.line_next,
&self.line_previous,
&self.line_start,
&self.line_end,
],
vec![
&self.insert_newline,
&self.delete_character_backward,
&self.delete_character_forward,
&self.paste,
],
vec![
&self.delete_word_backward,
&self.delete_word_forward,
&self.delete_after_cursor,
&self.delete_before_cursor,
],
]
}
}
impl Default for TextareaKeyMap {
fn default() -> Self {
Self {
character_forward: key::Binding::new(vec![
KeyPress::from(KeyCode::Right),
KeyPress::from((KeyCode::Char('f'), KeyModifiers::CONTROL)),
])
.with_help("→/ctrl+f", "character forward"),
character_backward: key::Binding::new(vec![
KeyPress::from(KeyCode::Left),
KeyPress::from((KeyCode::Char('b'), KeyModifiers::CONTROL)),
])
.with_help("←/ctrl+b", "character backward"),
word_forward: key::Binding::new(vec![
KeyPress::from((KeyCode::Right, KeyModifiers::ALT)),
KeyPress::from((KeyCode::Char('f'), KeyModifiers::ALT)),
])
.with_help("alt+→/alt+f", "word forward"),
word_backward: key::Binding::new(vec![
KeyPress::from((KeyCode::Left, KeyModifiers::ALT)),
KeyPress::from((KeyCode::Char('b'), KeyModifiers::ALT)),
])
.with_help("alt+←/alt+b", "word backward"),
line_next: key::Binding::new(vec![
KeyPress::from(KeyCode::Down),
KeyPress::from((KeyCode::Char('n'), KeyModifiers::CONTROL)),
])
.with_help("↓/ctrl+n", "next line"),
line_previous: key::Binding::new(vec![
KeyPress::from(KeyCode::Up),
KeyPress::from((KeyCode::Char('p'), KeyModifiers::CONTROL)),
])
.with_help("↑/ctrl+p", "previous line"),
delete_word_backward: key::Binding::new(vec![
KeyPress::from((KeyCode::Backspace, KeyModifiers::ALT)),
KeyPress::from((KeyCode::Char('w'), KeyModifiers::CONTROL)),
])
.with_help("alt+backspace/ctrl+w", "delete word backward"),
delete_word_forward: key::Binding::new(vec![
KeyPress::from((KeyCode::Delete, KeyModifiers::ALT)),
KeyPress::from((KeyCode::Char('d'), KeyModifiers::ALT)),
])
.with_help("alt+delete/alt+d", "delete word forward"),
delete_after_cursor: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('k'),
KeyModifiers::CONTROL,
))])
.with_help("ctrl+k", "delete after cursor"),
delete_before_cursor: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('u'),
KeyModifiers::CONTROL,
))])
.with_help("ctrl+u", "delete before cursor"),
insert_newline: key::Binding::new(vec![
KeyPress::from(KeyCode::Enter),
KeyPress::from((KeyCode::Char('m'), KeyModifiers::CONTROL)),
])
.with_help("enter/ctrl+m", "insert newline"),
delete_character_backward: key::Binding::new(vec![
KeyPress::from(KeyCode::Backspace),
KeyPress::from((KeyCode::Char('h'), KeyModifiers::CONTROL)),
])
.with_help("backspace/ctrl+h", "delete character backward"),
delete_character_forward: key::Binding::new(vec![
KeyPress::from(KeyCode::Delete),
KeyPress::from((KeyCode::Char('d'), KeyModifiers::CONTROL)),
])
.with_help("delete/ctrl+d", "delete character forward"),
line_start: key::Binding::new(vec![
KeyPress::from(KeyCode::Home),
KeyPress::from((KeyCode::Char('a'), KeyModifiers::CONTROL)),
])
.with_help("home/ctrl+a", "line start"),
line_end: key::Binding::new(vec![
KeyPress::from(KeyCode::End),
KeyPress::from((KeyCode::Char('e'), KeyModifiers::CONTROL)),
])
.with_help("end/ctrl+e", "line end"),
paste: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('v'),
KeyModifiers::CONTROL,
))])
.with_help("ctrl+v", "paste"),
input_begin: key::Binding::new(vec![
KeyPress::from((KeyCode::Char('<'), KeyModifiers::ALT)),
KeyPress::from((KeyCode::Home, KeyModifiers::CONTROL)),
])
.with_help("alt+</ctrl+home", "input begin"),
input_end: key::Binding::new(vec![
KeyPress::from((KeyCode::Char('>'), KeyModifiers::ALT)),
KeyPress::from((KeyCode::End, KeyModifiers::CONTROL)),
])
.with_help("alt+>/ctrl+end", "input end"),
capitalize_word_forward: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('c'),
KeyModifiers::ALT,
))])
.with_help("alt+c", "capitalize word forward"),
lowercase_word_forward: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('l'),
KeyModifiers::ALT,
))])
.with_help("alt+l", "lowercase word forward"),
uppercase_word_forward: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('u'),
KeyModifiers::ALT,
))])
.with_help("alt+u", "uppercase word forward"),
transpose_character_backward: key::Binding::new(vec![KeyPress::from((
KeyCode::Char('t'),
KeyModifiers::CONTROL,
))])
.with_help("ctrl+t", "transpose character backward"),
}
}
}
#[derive(Debug, Clone)]
pub struct TextareaStyle {
pub base: Style,
pub cursor_line: Style,
pub cursor_line_number: Style,
pub end_of_buffer: Style,
pub line_number: Style,
pub placeholder: Style,
pub prompt: Style,
pub text: Style,
}
impl TextareaStyle {
pub fn computed_cursor_line(&self) -> Style {
self.cursor_line
.clone()
.inherit(self.base.clone())
.inline(true)
}
pub fn computed_cursor_line_number(&self) -> Style {
self.cursor_line_number
.clone()
.inherit(self.cursor_line.clone())
.inherit(self.base.clone())
.inline(true)
}
pub fn computed_end_of_buffer(&self) -> Style {
self.end_of_buffer
.clone()
.inherit(self.base.clone())
.inline(true)
}
pub fn computed_line_number(&self) -> Style {
self.line_number
.clone()
.inherit(self.base.clone())
.inline(true)
}
pub fn computed_placeholder(&self) -> Style {
self.placeholder
.clone()
.inherit(self.base.clone())
.inline(true)
}
pub fn computed_prompt(&self) -> Style {
self.prompt.clone().inherit(self.base.clone()).inline(true)
}
pub fn computed_text(&self) -> Style {
self.text.clone().inherit(self.base.clone()).inline(true)
}
}
pub fn default_focused_style() -> TextareaStyle {
use lipgloss::AdaptiveColor;
TextareaStyle {
base: Style::new(),
cursor_line: Style::new().background(AdaptiveColor {
Light: "255",
Dark: "0",
}),
cursor_line_number: Style::new().foreground(AdaptiveColor {
Light: "240",
Dark: "",
}),
end_of_buffer: Style::new().foreground(AdaptiveColor {
Light: "254",
Dark: "0",
}),
line_number: Style::new().foreground(AdaptiveColor {
Light: "249",
Dark: "7",
}),
placeholder: Style::new().foreground(Color::from("240")),
prompt: Style::new().foreground(Color::from("7")),
text: Style::new(),
}
}
pub fn default_blurred_style() -> TextareaStyle {
use lipgloss::AdaptiveColor;
TextareaStyle {
base: Style::new(),
cursor_line: Style::new().foreground(AdaptiveColor {
Light: "245",
Dark: "7",
}),
cursor_line_number: Style::new().foreground(AdaptiveColor {
Light: "249",
Dark: "7",
}),
end_of_buffer: Style::new().foreground(AdaptiveColor {
Light: "254",
Dark: "0",
}),
line_number: Style::new().foreground(AdaptiveColor {
Light: "249",
Dark: "7",
}),
placeholder: Style::new().foreground(Color::from("240")),
prompt: Style::new().foreground(Color::from("7")),
text: Style::new().foreground(AdaptiveColor {
Light: "245",
Dark: "7",
}),
}
}
pub fn default_key_map() -> TextareaKeyMap {
TextareaKeyMap::default()
}
pub fn is_word_boundary(ch: char) -> bool {
ch.is_whitespace() || ch.is_ascii_punctuation()
}
pub fn word_start(text: &str, pos: usize) -> usize {
if pos == 0 {
return 0;
}
let chars: Vec<char> = text.chars().collect();
let mut i = pos.saturating_sub(1);
while i > 0 && !is_word_boundary(chars[i]) {
i -= 1;
}
if i > 0 && is_word_boundary(chars[i]) {
i + 1
} else {
i
}
}
pub fn word_end(text: &str, pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect();
let mut i = pos;
while i < chars.len() && !is_word_boundary(chars[i]) {
i += 1;
}
i
}
pub fn clamp<T: Ord>(value: T, min: T, max: T) -> T {
if value < min {
min
} else if value > max {
max
} else {
value
}
}
pub fn repeat_spaces(n: usize) -> Vec<char> {
std::iter::repeat_n(' ', n).collect()
}