1use crate::key::{self, KeyPress};
33use crossterm::event::{KeyCode, KeyModifiers};
34use lipgloss_extras::prelude::*;
35
36#[derive(Debug, Clone)]
38pub struct TextareaKeyMap {
39 pub character_backward: key::Binding,
41 pub character_forward: key::Binding,
43 pub delete_after_cursor: key::Binding,
45 pub delete_before_cursor: key::Binding,
47 pub delete_character_backward: key::Binding,
49 pub delete_character_forward: key::Binding,
51 pub delete_word_backward: key::Binding,
53 pub delete_word_forward: key::Binding,
55 pub insert_newline: key::Binding,
57 pub line_end: key::Binding,
59 pub line_next: key::Binding,
61 pub line_previous: key::Binding,
63 pub line_start: key::Binding,
65 pub paste: key::Binding,
67 pub word_backward: key::Binding,
69 pub word_forward: key::Binding,
71 pub input_begin: key::Binding,
73 pub input_end: key::Binding,
75 pub uppercase_word_forward: key::Binding,
78 pub lowercase_word_forward: key::Binding,
80 pub capitalize_word_forward: key::Binding,
82 pub transpose_character_backward: key::Binding,
84}
85
86impl crate::key::KeyMap for TextareaKeyMap {
88 fn short_help(&self) -> Vec<&key::Binding> {
89 vec![
90 &self.character_backward,
91 &self.character_forward,
92 &self.line_next,
93 &self.line_previous,
94 &self.insert_newline,
95 &self.delete_character_backward,
96 ]
97 }
98
99 fn full_help(&self) -> Vec<Vec<&key::Binding>> {
100 vec![
101 vec![
102 &self.character_backward,
103 &self.character_forward,
104 &self.word_backward,
105 &self.word_forward,
106 ],
107 vec![
108 &self.line_next,
109 &self.line_previous,
110 &self.line_start,
111 &self.line_end,
112 ],
113 vec![
114 &self.insert_newline,
115 &self.delete_character_backward,
116 &self.delete_character_forward,
117 &self.paste,
118 ],
119 vec![
120 &self.delete_word_backward,
121 &self.delete_word_forward,
122 &self.delete_after_cursor,
123 &self.delete_before_cursor,
124 ],
125 ]
126 }
127}
128
129impl Default for TextareaKeyMap {
130 fn default() -> Self {
131 Self {
132 character_forward: key::Binding::new(vec![
133 KeyPress::from(KeyCode::Right),
134 KeyPress::from((KeyCode::Char('f'), KeyModifiers::CONTROL)),
135 ])
136 .with_help("→/ctrl+f", "character forward"),
137
138 character_backward: key::Binding::new(vec![
139 KeyPress::from(KeyCode::Left),
140 KeyPress::from((KeyCode::Char('b'), KeyModifiers::CONTROL)),
141 ])
142 .with_help("←/ctrl+b", "character backward"),
143
144 word_forward: key::Binding::new(vec![
145 KeyPress::from((KeyCode::Right, KeyModifiers::ALT)),
146 KeyPress::from((KeyCode::Char('f'), KeyModifiers::ALT)),
147 ])
148 .with_help("alt+→/alt+f", "word forward"),
149
150 word_backward: key::Binding::new(vec![
151 KeyPress::from((KeyCode::Left, KeyModifiers::ALT)),
152 KeyPress::from((KeyCode::Char('b'), KeyModifiers::ALT)),
153 ])
154 .with_help("alt+←/alt+b", "word backward"),
155
156 line_next: key::Binding::new(vec![
157 KeyPress::from(KeyCode::Down),
158 KeyPress::from((KeyCode::Char('n'), KeyModifiers::CONTROL)),
159 ])
160 .with_help("↓/ctrl+n", "next line"),
161
162 line_previous: key::Binding::new(vec![
163 KeyPress::from(KeyCode::Up),
164 KeyPress::from((KeyCode::Char('p'), KeyModifiers::CONTROL)),
165 ])
166 .with_help("↑/ctrl+p", "previous line"),
167
168 delete_word_backward: key::Binding::new(vec![
169 KeyPress::from((KeyCode::Backspace, KeyModifiers::ALT)),
170 KeyPress::from((KeyCode::Char('w'), KeyModifiers::CONTROL)),
171 ])
172 .with_help("alt+backspace/ctrl+w", "delete word backward"),
173
174 delete_word_forward: key::Binding::new(vec![
175 KeyPress::from((KeyCode::Delete, KeyModifiers::ALT)),
176 KeyPress::from((KeyCode::Char('d'), KeyModifiers::ALT)),
177 ])
178 .with_help("alt+delete/alt+d", "delete word forward"),
179
180 delete_after_cursor: key::Binding::new(vec![KeyPress::from((
181 KeyCode::Char('k'),
182 KeyModifiers::CONTROL,
183 ))])
184 .with_help("ctrl+k", "delete after cursor"),
185
186 delete_before_cursor: key::Binding::new(vec![KeyPress::from((
187 KeyCode::Char('u'),
188 KeyModifiers::CONTROL,
189 ))])
190 .with_help("ctrl+u", "delete before cursor"),
191
192 insert_newline: key::Binding::new(vec![
193 KeyPress::from(KeyCode::Enter),
194 KeyPress::from((KeyCode::Char('m'), KeyModifiers::CONTROL)),
195 ])
196 .with_help("enter/ctrl+m", "insert newline"),
197
198 delete_character_backward: key::Binding::new(vec![
199 KeyPress::from(KeyCode::Backspace),
200 KeyPress::from((KeyCode::Char('h'), KeyModifiers::CONTROL)),
201 ])
202 .with_help("backspace/ctrl+h", "delete character backward"),
203
204 delete_character_forward: key::Binding::new(vec![
205 KeyPress::from(KeyCode::Delete),
206 KeyPress::from((KeyCode::Char('d'), KeyModifiers::CONTROL)),
207 ])
208 .with_help("delete/ctrl+d", "delete character forward"),
209
210 line_start: key::Binding::new(vec![
211 KeyPress::from(KeyCode::Home),
212 KeyPress::from((KeyCode::Char('a'), KeyModifiers::CONTROL)),
213 ])
214 .with_help("home/ctrl+a", "line start"),
215
216 line_end: key::Binding::new(vec![
217 KeyPress::from(KeyCode::End),
218 KeyPress::from((KeyCode::Char('e'), KeyModifiers::CONTROL)),
219 ])
220 .with_help("end/ctrl+e", "line end"),
221
222 paste: key::Binding::new(vec![KeyPress::from((
223 KeyCode::Char('v'),
224 KeyModifiers::CONTROL,
225 ))])
226 .with_help("ctrl+v", "paste"),
227
228 input_begin: key::Binding::new(vec![
229 KeyPress::from((KeyCode::Char('<'), KeyModifiers::ALT)),
230 KeyPress::from((KeyCode::Home, KeyModifiers::CONTROL)),
231 ])
232 .with_help("alt+</ctrl+home", "input begin"),
233
234 input_end: key::Binding::new(vec![
235 KeyPress::from((KeyCode::Char('>'), KeyModifiers::ALT)),
236 KeyPress::from((KeyCode::End, KeyModifiers::CONTROL)),
237 ])
238 .with_help("alt+>/ctrl+end", "input end"),
239
240 capitalize_word_forward: key::Binding::new(vec![KeyPress::from((
241 KeyCode::Char('c'),
242 KeyModifiers::ALT,
243 ))])
244 .with_help("alt+c", "capitalize word forward"),
245
246 lowercase_word_forward: key::Binding::new(vec![KeyPress::from((
247 KeyCode::Char('l'),
248 KeyModifiers::ALT,
249 ))])
250 .with_help("alt+l", "lowercase word forward"),
251
252 uppercase_word_forward: key::Binding::new(vec![KeyPress::from((
253 KeyCode::Char('u'),
254 KeyModifiers::ALT,
255 ))])
256 .with_help("alt+u", "uppercase word forward"),
257
258 transpose_character_backward: key::Binding::new(vec![KeyPress::from((
259 KeyCode::Char('t'),
260 KeyModifiers::CONTROL,
261 ))])
262 .with_help("ctrl+t", "transpose character backward"),
263 }
264 }
265}
266
267#[derive(Debug, Clone)]
269pub struct TextareaStyle {
270 pub base: Style,
272 pub cursor_line: Style,
274 pub cursor_line_number: Style,
276 pub end_of_buffer: Style,
278 pub line_number: Style,
280 pub placeholder: Style,
282 pub prompt: Style,
284 pub text: Style,
286}
287
288impl TextareaStyle {
289 pub fn computed_cursor_line(&self) -> Style {
291 self.cursor_line
292 .clone()
293 .inherit(self.base.clone())
294 .inline(true)
295 }
296
297 pub fn computed_cursor_line_number(&self) -> Style {
299 self.cursor_line_number
300 .clone()
301 .inherit(self.cursor_line.clone())
302 .inherit(self.base.clone())
303 .inline(true)
304 }
305
306 pub fn computed_end_of_buffer(&self) -> Style {
308 self.end_of_buffer
309 .clone()
310 .inherit(self.base.clone())
311 .inline(true)
312 }
313
314 pub fn computed_line_number(&self) -> Style {
316 self.line_number
317 .clone()
318 .inherit(self.base.clone())
319 .inline(true)
320 }
321
322 pub fn computed_placeholder(&self) -> Style {
324 self.placeholder
325 .clone()
326 .inherit(self.base.clone())
327 .inline(true)
328 }
329
330 pub fn computed_prompt(&self) -> Style {
332 self.prompt.clone().inherit(self.base.clone()).inline(true)
333 }
334
335 pub fn computed_text(&self) -> Style {
337 self.text.clone().inherit(self.base.clone()).inline(true)
338 }
339}
340
341pub fn default_focused_style() -> TextareaStyle {
343 use lipgloss::AdaptiveColor;
344
345 TextareaStyle {
346 base: Style::new(),
347 cursor_line: Style::new().background(AdaptiveColor {
348 Light: "255",
349 Dark: "0",
350 }),
351 cursor_line_number: Style::new().foreground(AdaptiveColor {
352 Light: "240",
353 Dark: "",
354 }),
355 end_of_buffer: Style::new().foreground(AdaptiveColor {
356 Light: "254",
357 Dark: "0",
358 }),
359 line_number: Style::new().foreground(AdaptiveColor {
360 Light: "249",
361 Dark: "7",
362 }),
363 placeholder: Style::new().foreground(Color::from("240")),
364 prompt: Style::new().foreground(Color::from("7")),
365 text: Style::new(),
366 }
367}
368
369pub fn default_blurred_style() -> TextareaStyle {
371 use lipgloss::AdaptiveColor;
372
373 TextareaStyle {
374 base: Style::new(),
375 cursor_line: Style::new().foreground(AdaptiveColor {
376 Light: "245",
377 Dark: "7",
378 }),
379 cursor_line_number: Style::new().foreground(AdaptiveColor {
380 Light: "249",
381 Dark: "7",
382 }),
383 end_of_buffer: Style::new().foreground(AdaptiveColor {
384 Light: "254",
385 Dark: "0",
386 }),
387 line_number: Style::new().foreground(AdaptiveColor {
388 Light: "249",
389 Dark: "7",
390 }),
391 placeholder: Style::new().foreground(Color::from("240")),
392 prompt: Style::new().foreground(Color::from("7")),
393 text: Style::new().foreground(AdaptiveColor {
394 Light: "245",
395 Dark: "7",
396 }),
397 }
398}
399
400pub fn default_key_map() -> TextareaKeyMap {
402 TextareaKeyMap::default()
403}
404
405pub fn is_word_boundary(ch: char) -> bool {
407 ch.is_whitespace() || ch.is_ascii_punctuation()
408}
409
410pub fn word_start(text: &str, pos: usize) -> usize {
412 if pos == 0 {
413 return 0;
414 }
415
416 let chars: Vec<char> = text.chars().collect();
417 let mut i = pos.saturating_sub(1);
418
419 while i > 0 && !is_word_boundary(chars[i]) {
420 i -= 1;
421 }
422
423 if i > 0 && is_word_boundary(chars[i]) {
424 i + 1
425 } else {
426 i
427 }
428}
429
430pub fn word_end(text: &str, pos: usize) -> usize {
432 let chars: Vec<char> = text.chars().collect();
433 let mut i = pos;
434
435 while i < chars.len() && !is_word_boundary(chars[i]) {
436 i += 1;
437 }
438
439 i
440}
441
442pub fn clamp<T: Ord>(value: T, min: T, max: T) -> T {
444 if value < min {
445 min
446 } else if value > max {
447 max
448 } else {
449 value
450 }
451}
452
453pub fn repeat_spaces(n: usize) -> Vec<char> {
455 std::iter::repeat_n(' ', n).collect()
456}