1use crate::key::{self, KeyPress};
33use crossterm::event::{KeyCode, KeyModifiers};
34use lipgloss::Style;
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 TextareaStyle {
344 base: Style::new(),
345 cursor_line: Style::new().background("#2a2a2a"),
346 cursor_line_number: Style::new().foreground("#666666"),
347 end_of_buffer: Style::new().foreground("#3c3c3c"),
348 line_number: Style::new().foreground("#666666"),
349 placeholder: Style::new().foreground("#666666"),
350 prompt: Style::new().foreground("#04b575"),
351 text: Style::new(),
352 }
353}
354
355pub fn default_blurred_style() -> TextareaStyle {
357 TextareaStyle {
358 base: Style::new(),
359 cursor_line: Style::new(),
360 cursor_line_number: Style::new().foreground("#3c3c3c"),
361 end_of_buffer: Style::new().foreground("#3c3c3c"),
362 line_number: Style::new().foreground("#3c3c3c"),
363 placeholder: Style::new().foreground("#666666"),
364 prompt: Style::new().foreground("#666666"),
365 text: Style::new(),
366 }
367}
368
369pub fn default_key_map() -> TextareaKeyMap {
371 TextareaKeyMap::default()
372}
373
374pub fn is_word_boundary(ch: char) -> bool {
376 ch.is_whitespace() || ch.is_ascii_punctuation()
377}
378
379pub fn word_start(text: &str, pos: usize) -> usize {
381 if pos == 0 {
382 return 0;
383 }
384
385 let chars: Vec<char> = text.chars().collect();
386 let mut i = pos.saturating_sub(1);
387
388 while i > 0 && !is_word_boundary(chars[i]) {
389 i -= 1;
390 }
391
392 if i > 0 && is_word_boundary(chars[i]) {
393 i + 1
394 } else {
395 i
396 }
397}
398
399pub fn word_end(text: &str, pos: usize) -> usize {
401 let chars: Vec<char> = text.chars().collect();
402 let mut i = pos;
403
404 while i < chars.len() && !is_word_boundary(chars[i]) {
405 i += 1;
406 }
407
408 i
409}
410
411pub fn clamp<T: Ord>(value: T, min: T, max: T) -> T {
413 if value < min {
414 min
415 } else if value > max {
416 max
417 } else {
418 value
419 }
420}
421
422pub fn repeat_spaces(n: usize) -> Vec<char> {
424 std::iter::repeat_n(' ', n).collect()
425}