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 Yank,
122}
123
124#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct StateChanged {
127 pub value: bool,
128 pub cursor: bool,
129}
130
131pub type InputResponse = Option<StateChanged>;
132
133#[derive(Default, Debug, Clone)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147pub struct Input {
148 value: Value,
149 cursor: usize,
151 yank: Value,
152 last_was_cut: bool,
153}
154
155impl Input {
156 pub fn new(value: String) -> Self {
159 let value = Value::new(value);
160 let cursor = value.chars();
161 Self {
162 value,
163 cursor,
164 yank: Value::default(),
165 last_was_cut: false,
166 }
167 }
168
169 pub fn with_value(mut self, value: String) -> Self {
172 self.value = Value::new(value);
173 self.cursor = self.value.chars();
174 self
175 }
176
177 pub fn with_cursor(mut self, cursor: usize) -> Self {
180 self.cursor = cursor.min(self.value.chars());
181 self
182 }
183
184 pub fn reset(&mut self) {
186 self.cursor = Default::default();
187 self.value = Default::default();
188 }
189
190 pub fn value_and_reset(&mut self) -> String {
192 let val = self.value.as_str().to_owned();
193 self.reset();
194 val
195 }
196
197 fn add_to_yank(&mut self, deleted: String, side: Side) {
198 if self.last_was_cut {
199 match side {
200 Side::Left => self.yank.edit().insert_str(0, &deleted),
201 Side::Right => self.yank.edit().push_str(&deleted),
202 }
203 } else {
204 self.yank = Value::new(deleted);
205 }
206 }
207
208 fn set_last_was_cut(&mut self, req: InputRequest) {
209 use InputRequest::*;
210 self.last_was_cut = matches!(
211 req,
212 DeleteLine | DeletePrevWord | DeleteNextWord | DeleteTillEnd
213 );
214 }
215
216 pub fn handle(&mut self, req: InputRequest) -> InputResponse {
218 use InputRequest::*;
219 let result = match req {
220 SetCursor(pos) => {
221 let pos = pos.min(self.value.chars());
222 if self.cursor == pos {
223 None
224 } else {
225 self.cursor = pos;
226 Some(StateChanged {
227 value: false,
228 cursor: true,
229 })
230 }
231 }
232 InsertChar(c) => {
233 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
234 self.value.edit().insert(byte, c);
235 self.cursor += 1;
236 Some(StateChanged {
237 value: true,
238 cursor: true,
239 })
240 }
241
242 DeletePrevChar => {
243 let s = self.value.as_str();
244 let byte = codepoint_to_byte(s, self.cursor);
245 let prev = prev_grapheme(s, byte)?;
246 let removed = s[prev..byte].chars().count();
247 self.value.edit().replace_range(prev..byte, "");
248 self.cursor -= removed;
249 Some(StateChanged {
250 value: true,
251 cursor: true,
252 })
253 }
254
255 DeleteNextChar => {
256 let s = self.value.as_str();
257 let byte = codepoint_to_byte(s, self.cursor);
258 let next = next_grapheme(s, byte)?;
259 self.value.edit().replace_range(byte..next, "");
260 Some(StateChanged {
261 value: true,
262 cursor: false,
263 })
264 }
265
266 GoToPrevChar => {
267 let s = self.value.as_str();
268 let byte = codepoint_to_byte(s, self.cursor);
269 let prev = prev_grapheme(s, byte)?;
270 self.cursor -= s[prev..byte].chars().count();
271 Some(StateChanged {
272 value: false,
273 cursor: true,
274 })
275 }
276
277 GoToPrevWord => {
278 let s = self.value.as_str();
279 let byte = codepoint_to_byte(s, self.cursor);
280 let prev = prev_word_byte(s, byte);
281 if self.cursor == 0 {
282 None
283 } else {
284 self.cursor = byte_to_codepoint(s, prev);
285 Some(StateChanged {
286 value: false,
287 cursor: true,
288 })
289 }
290 }
291
292 GoToNextChar => {
293 let s = self.value.as_str();
294 let byte = codepoint_to_byte(s, self.cursor);
295 let next = next_grapheme(s, byte)?;
296 self.cursor += s[byte..next].chars().count();
297 Some(StateChanged {
298 value: false,
299 cursor: true,
300 })
301 }
302
303 GoToNextWord => {
304 let s = self.value.as_str();
305 let byte = codepoint_to_byte(s, self.cursor);
306 let next = next_word_byte(s, byte);
307 if self.cursor == self.value.chars() {
308 None
309 } else {
310 self.cursor = byte_to_codepoint(s, next);
311 Some(StateChanged {
312 value: false,
313 cursor: true,
314 })
315 }
316 }
317
318 DeleteLine => {
319 if self.value.as_str().is_empty() {
320 None
321 } else {
322 let side = if self.cursor == self.value.chars() {
323 Side::Left
324 } else {
325 Side::Right
326 };
327 self.add_to_yank(self.value.as_str().to_owned(), side);
328 self.value.edit().clear();
329 self.cursor = 0;
330 Some(StateChanged {
331 value: true,
332 cursor: true,
333 })
334 }
335 }
336
337 DeletePrevWord => {
338 if self.cursor == 0 {
339 None
340 } else {
341 let s = self.value.as_str();
342 let byte = codepoint_to_byte(s, self.cursor);
343 let prev = prev_word_byte(s, byte);
344 let deleted = s[prev..byte].to_string();
345 self.add_to_yank(deleted, Side::Left);
346 self.value.edit().replace_range(prev..byte, "");
347 self.cursor = byte_to_codepoint(self.value.as_str(), prev);
348 Some(StateChanged {
349 value: true,
350 cursor: true,
351 })
352 }
353 }
354
355 DeleteNextWord => {
356 let s = self.value.as_str();
357 let byte = codepoint_to_byte(s, self.cursor);
358 let next = next_word_byte(s, byte);
359 if self.cursor == self.value.chars() {
360 None
361 } else {
362 let deleted = s[byte..next].to_string();
363 self.add_to_yank(deleted, Side::Right);
364 self.value.edit().replace_range(byte..next, "");
365 Some(StateChanged {
366 value: true,
367 cursor: false,
368 })
369 }
370 }
371
372 GoToStart => {
373 if self.cursor == 0 {
374 None
375 } else {
376 self.cursor = 0;
377 Some(StateChanged {
378 value: false,
379 cursor: true,
380 })
381 }
382 }
383
384 GoToEnd => {
385 let count = self.value.chars();
386 if self.cursor == count {
387 None
388 } else {
389 self.cursor = count;
390 Some(StateChanged {
391 value: false,
392 cursor: true,
393 })
394 }
395 }
396
397 DeleteTillEnd => {
398 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
399 let deleted = self.value.as_str()[byte..].to_string();
400 self.add_to_yank(deleted, Side::Right);
401 self.value.edit().truncate(byte);
402 Some(StateChanged {
403 value: true,
404 cursor: false,
405 })
406 }
407
408 Yank => {
409 if self.yank.as_str().is_empty() {
410 None
411 } else {
412 let byte = codepoint_to_byte(self.value.as_str(), self.cursor);
413 self.value.edit().insert_str(byte, self.yank.as_str());
414 self.cursor += self.yank.chars();
415 Some(StateChanged {
416 value: true,
417 cursor: true,
418 })
419 }
420 }
421 };
422 self.set_last_was_cut(req);
423 result
424 }
425
426 pub fn value(&self) -> &str {
428 self.value.as_str()
429 }
430
431 pub fn cursor(&self) -> usize {
436 self.cursor
437 }
438
439 pub fn visual_cursor(&self) -> usize {
442 if self.cursor == 0 {
443 return 0;
444 }
445
446 let s = self.value.as_str();
447 unicode_width::UnicodeWidthStr::width(unsafe {
449 s.get_unchecked(
450 0..s.char_indices()
451 .nth(self.cursor)
452 .map_or_else(|| s.len(), |(index, _)| index),
453 )
454 })
455 }
456
457 pub fn visual_scroll(&self, width: usize) -> usize {
459 let scroll = (self.visual_cursor()).max(width) - width;
460 let mut uscroll = 0;
461 let mut chars = self.value().chars();
462
463 while uscroll < scroll {
464 match chars.next() {
465 Some(c) => {
466 uscroll += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
467 }
468 None => break,
469 }
470 }
471 uscroll
472 }
473}
474
475impl From<Input> for String {
476 fn from(input: Input) -> Self {
477 input.value.into()
478 }
479}
480
481impl From<String> for Input {
482 fn from(value: String) -> Self {
483 Self::new(value)
484 }
485}
486
487impl From<&str> for Input {
488 fn from(value: &str) -> Self {
489 Self::new(value.into())
490 }
491}
492
493impl std::fmt::Display for Input {
494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495 self.value.as_str().fmt(f)
496 }
497}
498
499#[cfg(test)]
500mod tests;