1mod value;
41
42use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation};
43
44use self::value::Value;
45
46fn prev_grapheme(s: &str, byte: usize) -> Option<usize> {
47 GraphemeCursor::new(byte, s.len(), true)
48 .prev_boundary(s, 0)
49 .ok()
50 .flatten()
51}
52
53fn next_grapheme(s: &str, byte: usize) -> Option<usize> {
54 GraphemeCursor::new(byte, s.len(), true)
55 .next_boundary(s, 0)
56 .ok()
57 .flatten()
58}
59
60fn is_word(s: &str) -> bool {
61 s.chars()
62 .any(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
63}
64
65fn prev_word_byte(s: &str, byte: usize) -> usize {
66 let words = s
67 .split_word_bound_indices()
68 .filter(|(i, _)| *i < byte)
69 .rev();
70 for (i, word) in words {
71 if is_word(word) {
72 return i;
73 }
74 }
75 0
76}
77
78fn next_word_byte(s: &str, byte: usize) -> usize {
79 let words = s.split_word_bound_indices().filter(|(i, _)| *i > byte);
80 for (i, word) in words {
81 if is_word(word) {
82 return i;
83 }
84 }
85 s.len()
86}
87
88fn codepoint_to_byte(s: &str, n: usize) -> usize {
89 s.char_indices().nth(n).map_or(s.len(), |(i, _)| i)
90}
91
92fn byte_to_codepoint(s: &str, byte: usize) -> usize {
93 s[..byte].chars().count()
94}
95
96enum Side {
97 Left,
98 Right,
99}
100
101#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub enum InputRequest {
107 SetCursor(usize),
108 InsertChar(char),
109 GoToPrevChar,
110 GoToNextChar,
111 GoToPrevWord,
112 GoToNextWord,
113 GoToStart,
114 GoToEnd,
115 DeletePrevChar,
116 DeleteNextChar,
117 DeletePrevWord,
118 DeleteNextWord,
119 DeleteLine,
120 DeleteTillEnd,
121 DeleteFromStart,
122 Yank,
123}
124
125#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127pub struct StateChanged {
128 pub value: bool,
129 pub cursor: bool,
130}
131
132pub type InputResponse = Option<StateChanged>;
133
134#[derive(Default, Debug, Clone)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148pub struct Input {
149 value: Value,
150 cursor: usize,
152 yank: Value,
153 last_was_cut: bool,
154}
155
156impl Input {
157 pub fn new(value: String) -> Self {
160 let value = Value::new(value);
161 let cursor = value.chars();
162 Self {
163 value,
164 cursor,
165 yank: Value::default(),
166 last_was_cut: false,
167 }
168 }
169
170 pub fn with_value(mut self, value: String) -> Self {
173 self.value = Value::new(value);
174 self.cursor = self.value.chars();
175 self
176 }
177
178 pub fn with_cursor(mut self, cursor: usize) -> Self {
181 self.cursor = cursor.min(self.value.chars());
182 self
183 }
184
185 pub fn reset(&mut self) {
187 self.cursor = Default::default();
188 self.value = Default::default();
189 }
190
191 pub fn value_and_reset(&mut self) -> String {
193 let val = self.value.as_str().to_owned();
194 self.reset();
195 val
196 }
197
198 fn add_to_yank(&mut self, deleted: String, side: Side) {
199 if self.last_was_cut {
200 match side {
201 Side::Left => self.yank.edit().insert_str(0, &deleted),
202 Side::Right => self.yank.edit().push_str(&deleted),
203 }
204 } else {
205 self.yank = Value::new(deleted);
206 }
207 }
208
209 fn set_last_was_cut(&mut self, req: InputRequest) {
210 use InputRequest::*;
211 self.last_was_cut = matches!(
212 req,
213 DeleteLine | DeletePrevWord | DeleteNextWord | DeleteTillEnd
214 );
215 }
216
217 pub fn handle(&mut self, req: InputRequest) -> InputResponse {
219 use InputRequest::*;
220 let result = match req {
221 SetCursor(pos) => {
222 let pos = pos.min(self.value.chars());
223 if self.cursor == pos {
224 None
225 } else {
226 self.cursor = pos;
227 Some(StateChanged {
228 value: false,
229 cursor: true,
230 })
231 }
232 }
233 InsertChar(c) => {
234 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
235 self.value.edit().insert(byte, c);
236 self.cursor += 1;
237 Some(StateChanged {
238 value: true,
239 cursor: true,
240 })
241 }
242
243 DeletePrevChar => {
244 let s = self.value.as_str();
245 let byte = codepoint_to_byte(s, self.cursor);
246 let prev = prev_grapheme(s, byte)?;
247 let removed = s[prev..byte].chars().count();
248 self.value.edit().replace_range(prev..byte, "");
249 self.cursor -= removed;
250 Some(StateChanged {
251 value: true,
252 cursor: true,
253 })
254 }
255
256 DeleteNextChar => {
257 let s = self.value.as_str();
258 let byte = codepoint_to_byte(s, self.cursor);
259 let next = next_grapheme(s, byte)?;
260 self.value.edit().replace_range(byte..next, "");
261 Some(StateChanged {
262 value: true,
263 cursor: false,
264 })
265 }
266
267 GoToPrevChar => {
268 let s = self.value.as_str();
269 let byte = codepoint_to_byte(s, self.cursor);
270 let prev = prev_grapheme(s, byte)?;
271 self.cursor -= s[prev..byte].chars().count();
272 Some(StateChanged {
273 value: false,
274 cursor: true,
275 })
276 }
277
278 GoToPrevWord => {
279 let s = self.value.as_str();
280 let byte = codepoint_to_byte(s, self.cursor);
281 let prev = prev_word_byte(s, byte);
282 if self.cursor == 0 {
283 None
284 } else {
285 self.cursor = byte_to_codepoint(s, prev);
286 Some(StateChanged {
287 value: false,
288 cursor: true,
289 })
290 }
291 }
292
293 GoToNextChar => {
294 let s = self.value.as_str();
295 let byte = codepoint_to_byte(s, self.cursor);
296 let next = next_grapheme(s, byte)?;
297 self.cursor += s[byte..next].chars().count();
298 Some(StateChanged {
299 value: false,
300 cursor: true,
301 })
302 }
303
304 GoToNextWord => {
305 let s = self.value.as_str();
306 let byte = codepoint_to_byte(s, self.cursor);
307 let next = next_word_byte(s, byte);
308 if self.cursor == self.value.chars() {
309 None
310 } else {
311 self.cursor = byte_to_codepoint(s, next);
312 Some(StateChanged {
313 value: false,
314 cursor: true,
315 })
316 }
317 }
318
319 DeleteLine => {
320 if self.value.as_str().is_empty() {
321 None
322 } else {
323 let side = if self.cursor == self.value.chars() {
324 Side::Left
325 } else {
326 Side::Right
327 };
328 self.add_to_yank(self.value.as_str().to_owned(), side);
329 self.value.edit().clear();
330 self.cursor = 0;
331 Some(StateChanged {
332 value: true,
333 cursor: true,
334 })
335 }
336 }
337
338 DeletePrevWord => {
339 if self.cursor == 0 {
340 None
341 } else {
342 let s = self.value.as_str();
343 let byte = codepoint_to_byte(s, self.cursor);
344 let prev = prev_word_byte(s, byte);
345 let deleted = s[prev..byte].to_string();
346 self.add_to_yank(deleted, Side::Left);
347 self.value.edit().replace_range(prev..byte, "");
348 self.cursor = byte_to_codepoint(self.value.as_str(), prev);
349 Some(StateChanged {
350 value: true,
351 cursor: true,
352 })
353 }
354 }
355
356 DeleteNextWord => {
357 let s = self.value.as_str();
358 let byte = codepoint_to_byte(s, self.cursor);
359 let next = next_word_byte(s, byte);
360 if self.cursor == self.value.chars() {
361 None
362 } else {
363 let deleted = s[byte..next].to_string();
364 self.add_to_yank(deleted, Side::Right);
365 self.value.edit().replace_range(byte..next, "");
366 Some(StateChanged {
367 value: true,
368 cursor: false,
369 })
370 }
371 }
372
373 GoToStart => {
374 if self.cursor == 0 {
375 None
376 } else {
377 self.cursor = 0;
378 Some(StateChanged {
379 value: false,
380 cursor: true,
381 })
382 }
383 }
384
385 GoToEnd => {
386 let count = self.value.chars();
387 if self.cursor == count {
388 None
389 } else {
390 self.cursor = count;
391 Some(StateChanged {
392 value: false,
393 cursor: true,
394 })
395 }
396 }
397
398 DeleteTillEnd => {
399 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
400 let deleted = self.value.as_str()[byte..].to_string();
401 self.add_to_yank(deleted, Side::Right);
402 self.value.edit().truncate(byte);
403 Some(StateChanged {
404 value: true,
405 cursor: false,
406 })
407 }
408
409 DeleteFromStart => {
410 if self.cursor == 0 {
411 None
412 } else {
413 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
414 let deleted = self.value.edit().drain(..byte).collect();
415 self.add_to_yank(deleted, Side::Left);
416 self.cursor = 0;
417 Some(StateChanged {
418 value: true,
419 cursor: true,
420 })
421 }
422 }
423
424 Yank => {
425 if self.yank.as_str().is_empty() {
426 None
427 } else {
428 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
429 self.value.edit().insert_str(byte, self.yank.as_str());
430 self.cursor += self.yank.chars();
431 Some(StateChanged {
432 value: true,
433 cursor: true,
434 })
435 }
436 }
437 };
438 self.set_last_was_cut(req);
439 result
440 }
441
442 pub fn value(&self) -> &str {
444 self.value.as_str()
445 }
446
447 pub fn cursor(&self) -> usize {
452 self.cursor
453 }
454
455 pub fn visual_cursor(&self) -> usize {
458 if self.cursor == 0 {
459 return 0;
460 }
461
462 let s = self.value.as_str();
463 unicode_width::UnicodeWidthStr::width(unsafe {
465 s.get_unchecked(
466 0..s.char_indices()
467 .nth(self.cursor)
468 .map_or_else(|| s.len(), |(index, _)| index),
469 )
470 })
471 }
472
473 pub fn visual_scroll(&self, width: usize) -> usize {
475 let scroll = (self.visual_cursor()).max(width) - width;
476 let mut uscroll = 0;
477 let mut chars = self.value().chars();
478
479 while uscroll < scroll {
480 match chars.next() {
481 Some(c) => {
482 uscroll += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
483 }
484 None => break,
485 }
486 }
487 uscroll
488 }
489}
490
491impl From<Input> for String {
492 fn from(input: Input) -> Self {
493 input.value.into()
494 }
495}
496
497impl From<String> for Input {
498 fn from(value: String) -> Self {
499 Self::new(value)
500 }
501}
502
503impl From<&str> for Input {
504 fn from(value: &str) -> Self {
505 Self::new(value.into())
506 }
507}
508
509impl std::fmt::Display for Input {
510 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
511 self.value.as_str().fmt(f)
512 }
513}
514
515#[cfg(test)]
516mod tests;