rat_text/
text_mask_core.rs

1use crate::clipboard::{global_clipboard, Clipboard};
2use crate::core::{TextCore, TextString};
3use crate::grapheme::GlyphIter;
4use crate::text_mask_core::mask::{EditDirection, Mask, MaskToken};
5use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
6use crate::{upos_type, Cursor, Glyph, Grapheme, TextError, TextPosition, TextRange};
7use format_num_pattern::core::{clean_num, map_num};
8use format_num_pattern::{CurrencySym, NumberFormat, NumberSymbols};
9use std::borrow::Cow;
10use std::fmt;
11use std::iter::once;
12use std::ops::Range;
13use unicode_segmentation::UnicodeSegmentation;
14
15/// Text editing core.
16#[derive(Debug, Clone)]
17pub struct MaskedCore {
18    // text
19    masked: TextCore<TextString>,
20    // number symbols
21    sym: Option<NumberSymbols>,
22    // parsed mask
23    mask: Vec<MaskToken>,
24}
25
26impl Default for MaskedCore {
27    fn default() -> Self {
28        let mut value = TextCore::new(
29            Some(Box::new(UndoVec::new(99))),
30            Some(Box::new(global_clipboard())),
31        );
32        value.set_glyph_line_break(false);
33
34        Self {
35            masked: value,
36            sym: None,
37            mask: Default::default(),
38        }
39    }
40}
41
42impl MaskedCore {
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    /// Set the decimal separator and other symbols.
48    /// Only used for rendering and to map user input.
49    /// The value itself uses "."
50    pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
51        self.sym = Some(sym);
52    }
53
54    fn dec_sep(&self) -> char {
55        if let Some(sym) = &self.sym {
56            sym.decimal_sep
57        } else {
58            '.'
59        }
60    }
61
62    fn grp_sep(&self) -> char {
63        if let Some(sym) = &self.sym {
64            // fallback for empty grp-char.
65            // it would be really ugly, if we couldn't keep
66            //   mask-idx == grapheme-idx
67            sym.decimal_grp.unwrap_or(' ')
68        } else {
69            ','
70        }
71    }
72
73    fn neg_sym(&self) -> char {
74        if let Some(sym) = &self.sym {
75            sym.negative_sym
76        } else {
77            '-'
78        }
79    }
80
81    fn pos_sym(&self) -> char {
82        if let Some(sym) = &self.sym {
83            sym.positive_sym
84        } else {
85            ' '
86        }
87    }
88
89    /// Changes the mask.
90    /// Resets the value to a default.
91    pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
92        self.mask = Self::parse_mask(s.as_ref())?;
93        self.clear();
94        Ok(())
95    }
96
97    #[allow(clippy::needless_range_loop)]
98    fn parse_mask(mask_str: &str) -> Result<Vec<MaskToken>, fmt::Error> {
99        let mut out = Vec::<MaskToken>::new();
100
101        let mut start_sub = 0;
102        let mut start_sec = 0;
103        let mut sec_id = 0;
104        let mut last_mask = Mask::None;
105        let mut dec_dir = EditDirection::Rtol;
106        let mut esc = false;
107        let mut idx = 0;
108        for m in mask_str.graphemes(true).chain(once("")) {
109            let mask = if esc {
110                esc = false;
111                Mask::Separator(Box::from(m))
112            } else {
113                match m {
114                    "0" => Mask::Digit0(dec_dir),
115                    "9" => Mask::Digit(dec_dir),
116                    "#" => Mask::Numeric(dec_dir),
117                    "." => Mask::DecimalSep,
118                    "," => Mask::GroupingSep,
119                    "-" => Mask::Sign,
120                    "+" => Mask::Plus,
121                    "h" => Mask::Hex,
122                    "H" => Mask::Hex0,
123                    "o" => Mask::Oct,
124                    "O" => Mask::Oct0,
125                    "d" => Mask::Dec,
126                    "D" => Mask::Dec0,
127                    "l" => Mask::Letter,
128                    "a" => Mask::LetterOrDigit,
129                    "c" => Mask::LetterDigitSpace,
130                    "_" => Mask::AnyChar,
131                    "" => Mask::None,
132                    " " => Mask::Separator(Box::from(m)),
133                    "\\" => {
134                        esc = true;
135                        continue;
136                    }
137                    _ => return Err(fmt::Error),
138                }
139            };
140
141            match mask {
142                Mask::Digit0(_)
143                | Mask::Digit(_)
144                | Mask::Numeric(_)
145                | Mask::GroupingSep
146                | Mask::Sign
147                | Mask::Plus => {
148                    // no change
149                }
150                Mask::DecimalSep => {
151                    dec_dir = EditDirection::Ltor;
152                }
153                Mask::Hex0
154                | Mask::Hex
155                | Mask::Oct0
156                | Mask::Oct
157                | Mask::Dec0
158                | Mask::Dec
159                | Mask::Letter
160                | Mask::LetterOrDigit
161                | Mask::LetterDigitSpace
162                | Mask::AnyChar
163                | Mask::Separator(_) => {
164                    // reset to default number input direction
165                    dec_dir = EditDirection::Rtol
166                }
167                Mask::None => {
168                    // no change, doesn't matter
169                }
170            }
171
172            if matches!(mask, Mask::Separator(_)) || mask.section() != last_mask.section() {
173                for j in start_sec..idx {
174                    out[j].sec_id = sec_id;
175                    out[j].sec_start = start_sec as upos_type;
176                    out[j].sec_end = idx as upos_type;
177                }
178                sec_id += 1;
179                start_sec = idx;
180            }
181            if matches!(mask, Mask::Separator(_)) || mask.sub_section() != last_mask.sub_section() {
182                for j in start_sub..idx {
183                    out[j].sub_start = start_sub as upos_type;
184                    out[j].sub_end = idx as upos_type;
185                }
186                start_sub = idx;
187            }
188
189            let tok = MaskToken {
190                sec_id: 0,
191                sec_start: 0,
192                sec_end: 0,
193                sub_start: 0,
194                sub_end: 0,
195                peek_left: last_mask,
196                right: mask.clone(),
197                edit: mask.edit_value().into(),
198            };
199            out.push(tok);
200
201            idx += 1;
202            last_mask = mask;
203        }
204        for j in start_sec..out.len() {
205            out[j].sec_id = sec_id;
206            out[j].sec_start = start_sec as upos_type;
207            out[j].sec_end = mask_str.graphemes(true).count() as upos_type;
208        }
209        for j in start_sub..out.len() {
210            out[j].sub_start = start_sub as upos_type;
211            out[j].sub_end = mask_str.graphemes(true).count() as upos_type;
212        }
213
214        Ok(out)
215    }
216
217    /// Return the mask.
218    pub fn mask(&self) -> String {
219        use std::fmt::Write;
220
221        let mut buf = String::new();
222        for t in self.mask.iter() {
223            _ = write!(buf, "{}", t.right);
224        }
225        buf
226    }
227}
228
229impl MaskedCore {
230    /// Clipboard
231    pub fn set_clipboard(&mut self, clip: Option<Box<dyn Clipboard + 'static>>) {
232        self.masked.set_clipboard(clip);
233    }
234
235    /// Clipboard
236    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
237        self.masked.clipboard()
238    }
239}
240
241impl MaskedCore {
242    /// Undo
243    #[inline]
244    pub fn set_undo_buffer(&mut self, undo: Option<Box<dyn UndoBuffer>>) {
245        self.masked.set_undo_buffer(undo);
246    }
247
248    /// Set undo count
249    #[inline]
250    pub fn set_undo_count(&mut self, n: u32) {
251        self.masked.set_undo_count(n);
252    }
253
254    /// Begin a sequence of changes that should be undone in one go.
255    #[inline]
256    pub fn begin_undo_seq(&mut self) {
257        self.masked.begin_undo_seq();
258    }
259
260    /// End a sequence of changes that should be undone in one go.
261    #[inline]
262    pub fn end_undo_seq(&mut self) {
263        self.masked.end_undo_seq();
264    }
265
266    /// Undo
267    #[inline]
268    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
269        self.masked.undo_buffer()
270    }
271
272    /// Undo
273    #[inline]
274    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
275        self.masked.undo_buffer_mut()
276    }
277
278    /// Undo last.
279    pub fn undo(&mut self) -> bool {
280        self.masked.undo()
281    }
282
283    /// Redo last.
284    pub fn redo(&mut self) -> bool {
285        self.masked.redo()
286    }
287
288    /// Get last replay recording.
289    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
290        self.masked.recent_replay_log()
291    }
292
293    /// Replay a recording of changes.
294    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
295        self.masked.replay_log(replay)
296    }
297}
298
299impl MaskedCore {
300    /// Set all styles.
301    ///
302    /// The ranges are byte-ranges. The usize value is the index of the
303    /// actual style. Those are set with the widget.
304    #[inline]
305    pub fn set_styles(&mut self, new_styles: Vec<(Range<usize>, usize)>) {
306        self.masked.set_styles(new_styles);
307    }
308
309    /// Add a style for the given byte-range.
310    ///
311    /// The usize value is the index of the actual style.
312    /// Those are set at the widget.
313    #[inline]
314    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
315        self.masked.add_style(range, style);
316    }
317
318    /// Remove a style for the given byte-range.
319    ///
320    /// Range and style must match to be removed.
321    #[inline]
322    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
323        self.masked.remove_style(range, style);
324    }
325
326    /// Find all values for the given position.
327    ///
328    /// Creates a cache for the styles in range.
329    #[inline]
330    pub(crate) fn styles_at_page(&self, range: Range<usize>, pos: usize, buf: &mut Vec<usize>) {
331        self.masked.styles_at_page(range, pos, buf);
332    }
333
334    /// Find all styles that touch the given range.
335    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
336        self.masked.styles_in(range, buf)
337    }
338
339    /// Finds all styles for the given position.
340    #[inline]
341    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
342        self.masked.styles_at(byte_pos, buf);
343    }
344
345    /// Check if the given style applies at the position and
346    /// return the complete range for the style.
347    #[inline]
348    pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
349        self.masked.style_match(byte_pos, style)
350    }
351
352    /// List of all styles.
353    #[inline]
354    pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
355        self.masked.styles()
356    }
357}
358
359impl MaskedCore {
360    /// Set the cursor position.
361    /// The value is capped to the number of text lines and
362    /// the line-width for the given line.
363    ///
364    /// Returns true, if the cursor actually changed.
365    pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
366        self.masked
367            .set_cursor(TextPosition::new(cursor, 0), extend_selection)
368    }
369
370    // find default cursor position for a number range
371    fn number_cursor(&self, range: Range<upos_type>) -> upos_type {
372        for (i, t) in self.mask[range.start as usize..range.end as usize]
373            .iter()
374            .enumerate()
375            .rev()
376        {
377            match t.right {
378                Mask::Digit(EditDirection::Rtol)
379                | Mask::Digit0(EditDirection::Rtol)
380                | Mask::Numeric(EditDirection::Rtol) => {
381                    return range.start + i as upos_type + 1;
382                }
383                _ => {}
384            }
385        }
386        range.start
387    }
388
389    /// Get the default cursor for the section at the given cursor position,
390    /// if it is an editable section.
391    pub fn section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
392        if cursor as usize >= self.mask.len() {
393            return None;
394        }
395
396        let mask = &self.mask[cursor as usize];
397
398        if mask.right.is_number() {
399            Some(self.number_cursor(mask.sec_start..mask.sec_end))
400        } else if mask.right.is_separator() {
401            None
402        } else if mask.right.is_none() {
403            None
404        } else {
405            Some(mask.sec_start)
406        }
407    }
408
409    /// Get the default cursor position for the next editable section.
410    pub fn next_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
411        if cursor as usize >= self.mask.len() {
412            return None;
413        }
414
415        let mut mask = &self.mask[cursor as usize];
416        let mut next;
417        loop {
418            if mask.right.is_none() {
419                return None;
420            }
421
422            next = mask.sec_end;
423            mask = &self.mask[next as usize];
424
425            if mask.right.is_number() {
426                return Some(self.number_cursor(mask.sec_start..mask.sec_end));
427            } else if mask.right.is_separator() {
428                continue;
429            } else if mask.right.is_none() {
430                return None;
431            } else {
432                return Some(mask.sec_start);
433            }
434        }
435    }
436
437    /// Get the default cursor position for the next editable section.
438    pub fn prev_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
439        if cursor as usize >= self.mask.len() {
440            return None;
441        }
442
443        let mut prev = self.mask[cursor as usize].sec_start;
444        let mut mask = &self.mask[prev as usize];
445
446        loop {
447            if mask.peek_left.is_none() {
448                return None;
449            }
450
451            prev = self.mask[mask.sec_start as usize - 1].sec_start;
452            mask = &self.mask[prev as usize];
453
454            if mask.right.is_number() {
455                return Some(self.number_cursor(mask.sec_start..mask.sec_end));
456            } else if mask.right.is_separator() {
457                continue;
458            } else {
459                return Some(mask.sec_start);
460            }
461        }
462    }
463
464    /// Is the position at a word boundary?
465    pub fn is_section_boundary(&self, pos: upos_type) -> bool {
466        if pos == 0 {
467            return false;
468        }
469        if pos as usize >= self.mask.len() {
470            return false;
471        }
472        let prev = &self.mask[pos as usize - 1];
473        let mask = &self.mask[pos as usize];
474        prev.sec_id != mask.sec_id
475    }
476
477    /// Get the range for the section at the given cursor position,
478    /// if it is an editable section.
479    pub fn section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
480        if cursor as usize >= self.mask.len() {
481            return None;
482        }
483
484        let mask = &self.mask[cursor as usize];
485        if mask.right.is_number() {
486            Some(mask.sec_start..mask.sec_end)
487        } else if mask.right.is_separator() {
488            None
489        } else if mask.right.is_none() {
490            None
491        } else {
492            Some(mask.sec_start..mask.sec_end)
493        }
494    }
495
496    /// Get the default cursor position for the next editable section.
497    pub fn next_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
498        if cursor as usize >= self.mask.len() {
499            return None;
500        }
501
502        let mut mask = &self.mask[cursor as usize];
503        let mut next;
504        loop {
505            if mask.right.is_none() {
506                return None;
507            }
508
509            next = mask.sec_end;
510            mask = &self.mask[next as usize];
511
512            if mask.right.is_number() {
513                return Some(mask.sec_start..mask.sec_end);
514            } else if mask.right.is_separator() {
515                continue;
516            } else if mask.right.is_none() {
517                return None;
518            } else {
519                return Some(mask.sec_start..mask.sec_end);
520            }
521        }
522    }
523
524    /// Get the default cursor position for the next editable section.
525    pub fn prev_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
526        if cursor as usize >= self.mask.len() {
527            return None;
528        }
529
530        let mut prev = self.mask[cursor as usize].sec_start;
531        let mut mask = &self.mask[prev as usize];
532        loop {
533            if mask.peek_left.is_none() {
534                return None;
535            }
536
537            prev = self.mask[mask.sec_start as usize - 1].sec_start;
538            mask = &self.mask[prev as usize];
539
540            if mask.right.is_number() {
541                return Some(mask.sec_start..mask.sec_end);
542            } else if mask.right.is_separator() {
543                continue;
544            } else {
545                return Some(mask.sec_start..mask.sec_end);
546            }
547        }
548    }
549
550    /// Place cursor at decimal separator, if any.
551    /// 0 otherwise.
552    #[inline]
553    pub fn set_default_cursor(&mut self) {
554        if let Some(pos) = self.section_cursor(0) {
555            self.masked.set_cursor(TextPosition::new(pos, 0), false);
556        } else if let Some(pos) = self.next_section_cursor(0) {
557            self.masked.set_cursor(TextPosition::new(pos, 0), false);
558        } else {
559            self.masked.set_cursor(TextPosition::new(0, 0), false);
560        }
561    }
562
563    /// Cursor position as grapheme-idx.
564    #[inline]
565    pub fn cursor(&self) -> upos_type {
566        self.masked.cursor().x
567    }
568
569    /// Selection anchor
570    #[inline]
571    pub fn anchor(&self) -> upos_type {
572        self.masked.anchor().x
573    }
574
575    /// Any text selection.
576    #[inline]
577    pub fn has_selection(&self) -> bool {
578        self.masked.has_selection()
579    }
580
581    /// Select text.
582    #[inline]
583    pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
584        self.masked
585            .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
586    }
587
588    /// Select all text.
589    #[inline]
590    pub fn select_all(&mut self) -> bool {
591        self.masked.select_all()
592    }
593
594    /// Returns the selection as TextRange.
595    #[inline]
596    pub fn selection(&self) -> Range<upos_type> {
597        let selection = self.masked.selection();
598        selection.start.x..selection.end.x
599    }
600
601    /// Selection.
602    #[inline]
603    pub fn selected_text(&self) -> &str {
604        match self
605            .masked
606            .str_slice(self.masked.selection())
607            .expect("valid_range")
608        {
609            Cow::Borrowed(v) => v,
610            Cow::Owned(_) => {
611                unreachable!()
612            }
613        }
614    }
615}
616
617impl MaskedCore {
618    /// Empty.
619    #[inline]
620    pub fn is_empty(&self) -> bool {
621        self.masked.text().as_str() == self.default_value()
622    }
623
624    /// Grapheme position to byte position.
625    /// This is the (start,end) position of the single grapheme after pos.
626    #[inline]
627    pub fn byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
628        self.masked.byte_at(TextPosition::new(pos, 0))
629    }
630
631    /// Grapheme range to byte range.
632    #[inline]
633    pub fn bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
634        self.masked
635            .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
636    }
637
638    /// Byte position to grapheme position.
639    /// Returns the position that contains the given byte index.
640    #[inline]
641    pub fn byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
642        Ok(self.masked.byte_pos(byte)?.x)
643    }
644
645    /// Byte range to grapheme range.
646    #[inline]
647    pub fn byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
648        let r = self.masked.byte_range(bytes)?;
649        Ok(r.start.x..r.end.x)
650    }
651
652    /// Text slice as `Cow<str>`. Uses a byte range.
653    #[inline]
654    pub fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
655        self.masked.str_slice_byte(range)
656    }
657
658    /// A range of the text as `Cow<str>`
659    #[inline]
660    pub fn str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
661        self.masked
662            .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
663    }
664
665    /// Iterator for the glyphs of the lines in range.
666    /// Glyphs here a grapheme + display length.
667    #[inline]
668    pub fn glyphs(
669        &self,
670        rows: Range<upos_type>,
671        screen_offset: u16,
672        screen_width: u16,
673    ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
674        let grapheme_iter = self.masked.graphemes(
675            TextRange::new((0, rows.start), (0, rows.end)),
676            TextPosition::new(0, rows.start),
677        )?;
678
679        let mask_iter = self.mask.iter();
680
681        let sym_neg = || self.neg_sym().to_string();
682        let sym_dec = || self.dec_sep().to_string();
683        let sym_grp = || self.grp_sep().to_string();
684        let sym_pos = || self.pos_sym().to_string();
685
686        let iter = grapheme_iter
687            .zip(mask_iter)
688            .map(move |(g, t)| match (&t.right, g.grapheme()) {
689                (Mask::Numeric(_), "-") => Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()),
690                (Mask::DecimalSep, ".") => Grapheme::new(Cow::Owned(sym_dec()), g.text_bytes()),
691                (Mask::GroupingSep, ",") => Grapheme::new(Cow::Owned(sym_grp()), g.text_bytes()),
692                (Mask::GroupingSep, "-") => Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()),
693                (Mask::Sign, "-") => Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()),
694                (Mask::Sign, _) => Grapheme::new(Cow::Owned(sym_pos()), g.text_bytes()),
695                (_, _) => g,
696            });
697
698        let mut it = GlyphIter::new(TextPosition::new(0, rows.start), iter);
699        it.set_screen_offset(screen_offset);
700        it.set_screen_width(screen_width);
701        it.set_tabs(self.masked.tab_width());
702        it.set_show_ctrl(self.masked.glyph_ctrl());
703        it.set_line_break(self.masked.glyph_line_break());
704        Ok(it)
705    }
706
707    /// Iterator for the glyphs of the lines in range.
708    /// Glyphs here a grapheme + display length.
709    ///
710    /// This omits unnecessary white-space.
711    #[inline]
712    pub fn condensed_glyphs(
713        &self,
714        rows: Range<upos_type>,
715        screen_offset: u16,
716        screen_width: u16,
717    ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
718        let grapheme_iter = self.masked.graphemes(
719            TextRange::new((0, rows.start), (0, rows.end)),
720            TextPosition::new(0, rows.start),
721        )?;
722
723        let mask_iter = self.mask.iter();
724
725        let sym_neg = || self.neg_sym().to_string();
726        let sym_dec = || self.dec_sep().to_string();
727        let sym_grp = || self.grp_sep().to_string();
728        let sym_pos = || self.pos_sym().to_string();
729
730        let iter =
731            grapheme_iter
732                .zip(mask_iter)
733                .filter_map(move |(g, t)| match (&t.right, g.grapheme()) {
734                    (Mask::Numeric(_), "-") => {
735                        Some(Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()))
736                    }
737                    (Mask::DecimalSep, ".") => {
738                        Some(Grapheme::new(Cow::Owned(sym_dec()), g.text_bytes()))
739                    }
740                    (Mask::GroupingSep, ",") => {
741                        Some(Grapheme::new(Cow::Owned(sym_grp()), g.text_bytes()))
742                    }
743                    (Mask::GroupingSep, "-") => {
744                        Some(Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()))
745                    }
746                    (Mask::Sign, "-") => Some(Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes())),
747
748                    (Mask::Numeric(_), " ") => None,
749                    (Mask::Digit(_), " ") => None,
750                    (Mask::DecimalSep, " ") => None,
751                    (Mask::GroupingSep, " ") => None,
752                    (Mask::Sign, _) => {
753                        if self.pos_sym() != ' ' {
754                            Some(Grapheme::new(Cow::Owned(sym_pos()), g.text_bytes()))
755                        } else {
756                            None
757                        }
758                    }
759                    (Mask::Hex, " ") => None,
760                    (Mask::Oct, " ") => None,
761                    (Mask::Dec, " ") => None,
762
763                    (_, _) => Some(g),
764                });
765
766        let mut it = GlyphIter::new(TextPosition::new(0, rows.start), iter);
767        it.set_screen_offset(screen_offset);
768        it.set_screen_width(screen_width);
769        it.set_tabs(self.masked.tab_width());
770        it.set_show_ctrl(self.masked.glyph_ctrl());
771        it.set_line_break(self.masked.glyph_line_break());
772        Ok(it)
773    }
774
775    /// Get the grapheme at the given position.
776    #[inline]
777    pub fn grapheme_at(&self, pos: upos_type) -> Result<Option<Grapheme<'_>>, TextError> {
778        self.masked.grapheme_at(TextPosition::new(pos, 0))
779    }
780
781    /// Get a cursor over all the text with the current position set at pos.
782    #[inline]
783    pub fn text_graphemes(
784        &self,
785        pos: upos_type,
786    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
787        self.masked.text_graphemes(TextPosition::new(pos, 0))
788    }
789
790    /// Get a cursor over the text-range the current position set at pos.
791    #[inline]
792    pub fn graphemes(
793        &self,
794        range: Range<upos_type>,
795        pos: upos_type,
796    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
797        self.masked.graphemes(
798            TextRange::new((range.start, 0), (range.end, 0)),
799            TextPosition::new(pos, 0),
800        )
801    }
802
803    #[inline]
804    pub fn line_width(&self) -> upos_type {
805        self.masked.line_width(0).expect("valid_row")
806    }
807}
808
809impl MaskedCore {
810    /// Create a default value according to the mask.
811    #[inline]
812    fn default_value(&self) -> String {
813        MaskToken::empty_section(&self.mask)
814    }
815}
816
817impl MaskedCore {
818    /// Reset value but not the mask and width.
819    /// Resets offset and cursor position too.
820    #[inline]
821    pub fn clear(&mut self) {
822        self.masked
823            .set_text(TextString::new_string(self.default_value()));
824        self.set_default_cursor();
825    }
826
827    /// Copy of the text-value.
828    pub fn text(&self) -> &str {
829        self.masked.text().as_str()
830    }
831
832    /// Sets the value.
833    /// No checks if the value conforms to the mask.
834    /// If the value is too short it will be filled with space.
835    /// if the value is too long it will be truncated.
836    #[allow(clippy::comparison_chain)]
837    pub fn set_text<S: Into<String>>(&mut self, s: S) {
838        let mut text = s.into();
839        while text.graphemes(true).count() > self.mask.len().saturating_sub(1) {
840            text.pop();
841        }
842        while text.graphemes(true).count() < self.mask.len().saturating_sub(1) {
843            text.push(' ');
844        }
845        let len = text.graphemes(true).count();
846
847        assert_eq!(len, self.mask.len().saturating_sub(1));
848
849        self.masked.set_text(TextString::new_string(text));
850    }
851
852    /// Start at the cursor position and find a valid insert position for the input c.
853    /// Put the cursor at that position.
854    #[allow(clippy::if_same_then_else)]
855    pub fn advance_cursor(&mut self, c: char) -> bool {
856        if self.mask.is_empty() {
857            return false;
858        }
859
860        let mask_c = &self.mask[self.masked.cursor().x as usize];
861
862        let mut new_cursor = self.masked.cursor().x;
863
864        loop {
865            let mask = &self.mask[new_cursor as usize];
866
867            if self.can_insert_integer_left(mask, new_cursor, c) {
868                // At the gap between an integer field and something else.
869                // Integer fields are served first.
870                break;
871            } else if self.can_insert_integer(mask, new_cursor, c) {
872                // Insert position inside an integer field. After any spaces
873                // and the sign.
874                break;
875            } else if self.can_insert_sign(mask, new_cursor, c) {
876                // Can insert a sign here.
877                break;
878            } else if self.can_insert_decimal_sep(mask, c) {
879                // Decimal separator matches.
880                break;
881            } else if mask.right == Mask::GroupingSep {
882                // Never stop here.
883                new_cursor += 1;
884            } else if self.can_insert_separator(mask, c) {
885                break;
886            } else if self.can_move_left_in_fraction(mask_c, mask, new_cursor, c) {
887                // skip left
888                new_cursor -= 1;
889            } else if self.can_insert_fraction(mask_c, mask, c) {
890                break;
891            } else if self.can_insert_other(mask, c) {
892                break;
893            } else if mask.right == Mask::None {
894                // No better position found. Reset and break;
895                new_cursor = self.masked.cursor().x;
896                break;
897            } else {
898                new_cursor += 1;
899            }
900        }
901
902        self.masked
903            .set_cursor(TextPosition::new(new_cursor, 0), false)
904    }
905
906    /// Valid input for this mask.
907    fn is_valid_char(&self, mask: &Mask, c: char) -> bool {
908        match mask {
909            Mask::Digit0(_) => c.is_ascii_digit(),
910            Mask::Digit(_) => c.is_ascii_digit() || c == ' ',
911            Mask::Numeric(_) => c.is_ascii_digit() || c == self.neg_sym() || c == '-',
912            Mask::DecimalSep => c == self.dec_sep(),
913            Mask::GroupingSep => false,
914            Mask::Sign => c == self.neg_sym() || c == '-',
915            Mask::Plus => c == self.neg_sym() || c == '-',
916            Mask::Hex0 => c.is_ascii_hexdigit(),
917            Mask::Hex => c.is_ascii_hexdigit() || c == ' ',
918            Mask::Oct0 => c.is_digit(8),
919            Mask::Oct => c.is_digit(8) || c == ' ',
920            Mask::Dec0 => c.is_ascii_digit(),
921            Mask::Dec => c.is_ascii_digit() || c == ' ',
922            Mask::Letter => c.is_alphabetic(),
923            Mask::LetterOrDigit => c.is_alphanumeric(),
924            Mask::LetterDigitSpace => c.is_alphanumeric() || c == ' ',
925            Mask::AnyChar => true,
926            Mask::Separator(sep) => {
927                // ',' and '.' match any separator.
928                if c == '.' || c == ',' {
929                    true
930                } else if let Some(sepc) = sep.chars().next() {
931                    // todo: don't know better
932                    sepc == c
933                } else {
934                    false
935                }
936            }
937            Mask::None => false,
938        }
939    }
940
941    // Can insert other field types
942    #[inline]
943    fn can_insert_other(&self, mask: &MaskToken, c: char) -> bool {
944        match mask.right {
945            Mask::Hex0
946            | Mask::Hex
947            | Mask::Oct0
948            | Mask::Oct
949            | Mask::Dec0
950            | Mask::Dec
951            | Mask::Letter
952            | Mask::LetterOrDigit
953            | Mask::LetterDigitSpace
954            | Mask::AnyChar => self.is_valid_char(&mask.right, c),
955            _ => false,
956        }
957    }
958
959    // Can insert fraction.
960    #[inline]
961    fn can_insert_fraction(&self, mask_c: &MaskToken, mask: &MaskToken, c: char) -> bool {
962        if !mask.right.is_fraction() {
963            return false;
964        }
965        if !self.is_valid_char(&mask.right, c) {
966            return false;
967        }
968        // don't jump from integer to fraction
969        if mask_c.is_integer_part() {
970            return false;
971        }
972
973        true
974    }
975
976    // When inserting to the fraction we want to left-align
977    // the digits. This checks if a digit could possibly be
978    // inserted to the left of the current position.
979    #[inline]
980    fn can_move_left_in_fraction(
981        &self,
982        mask_c: &MaskToken,
983        mask: &MaskToken,
984        new_cursor: upos_type,
985        c: char,
986    ) -> bool {
987        if !mask.peek_left.is_fraction() {
988            return false;
989        }
990        if !self.is_valid_char(&mask.peek_left, c) {
991            return false;
992        }
993        // don't jump from integer to fraction
994        if mask_c.is_integer_part() {
995            return false;
996        }
997
998        let gl = self
999            .masked
1000            .grapheme_at(TextPosition::new(new_cursor - 1, 0))
1001            .expect("valid_position")
1002            .expect("grapheme");
1003
1004        // is there space to the left?
1005        if gl != " " {
1006            return false;
1007        }
1008
1009        true
1010    }
1011
1012    // Can input a sign here?
1013    #[inline]
1014    fn can_insert_sign<'a>(
1015        &'a self,
1016        mut mask: &'a MaskToken,
1017        new_cursor: upos_type,
1018        c: char,
1019    ) -> bool {
1020        if !self.is_valid_char(&Mask::Sign, c) {
1021            return false;
1022        }
1023        // boundary right/left. prefer right, change mask.
1024        if mask.peek_left.is_number() && (mask.right.is_ltor() || mask.right.is_none()) {
1025            mask = &self.mask[new_cursor as usize - 1];
1026        }
1027        if !mask.right.is_number() {
1028            return false;
1029        }
1030
1031        // check possible positions for the sign.
1032        for i in mask.sec_start..mask.sec_end {
1033            let t = &self.mask[i as usize];
1034            match t.right {
1035                Mask::Plus => return true,
1036                Mask::Sign => return true,
1037                Mask::Numeric(EditDirection::Rtol) => {
1038                    // Numeric fields can hold a sign.
1039                    // If they are not otherwise occupied.
1040                    let gi = self
1041                        .masked
1042                        .grapheme_at(TextPosition::new(i, 0))
1043                        .expect("valid_position")
1044                        .expect("grapheme");
1045
1046                    return t.right.can_drop(gi.grapheme()) || gi == "-";
1047                }
1048                _ => {}
1049            }
1050        }
1051
1052        false
1053    }
1054
1055    // Is this the correct input position for a rtol field
1056    #[inline]
1057    fn can_insert_integer(&self, mask: &MaskToken, new_cursor: upos_type, c: char) -> bool {
1058        if !mask.right.is_rtol() {
1059            return false;
1060        }
1061
1062        if !self.is_valid_char(&mask.right, c) {
1063            return false;
1064        }
1065
1066        let g = self
1067            .masked
1068            .grapheme_at(TextPosition::new(new_cursor, 0))
1069            .expect("valid_position")
1070            .expect("grapheme");
1071        if mask.right.can_drop(g.grapheme()) {
1072            return false;
1073        }
1074        if g == "-" {
1075            return false;
1076        }
1077
1078        true
1079    }
1080
1081    // Separator char matches
1082    #[inline]
1083    fn can_insert_separator(&self, mask: &MaskToken, c: char) -> bool {
1084        if !matches!(mask.right, Mask::Separator(_)) {
1085            return false;
1086        }
1087        if !self.is_valid_char(&mask.right, c) {
1088            return false;
1089        }
1090        true
1091    }
1092
1093    // Can insert a decimal separator.
1094    #[inline]
1095    fn can_insert_decimal_sep(&self, mask: &MaskToken, c: char) -> bool {
1096        if mask.right != Mask::DecimalSep {
1097            return false;
1098        }
1099        if !self.is_valid_char(&mask.right, c) {
1100            return false;
1101        }
1102        true
1103    }
1104
1105    // Can edit the field left of the cursor.
1106    #[inline]
1107    fn can_insert_integer_left(&self, mask: &MaskToken, new_cursor: upos_type, c: char) -> bool {
1108        if !mask.peek_left.is_rtol() {
1109            return false;
1110        }
1111        if !mask.right.is_ltor() && !mask.right.is_none() {
1112            return false;
1113        }
1114
1115        let left = &self.mask[new_cursor as usize - 1];
1116        if !self.is_valid_char(&left.right, c) {
1117            return false;
1118        }
1119
1120        let mask0 = &self.mask[left.sub_start as usize];
1121        let g0 = self
1122            .masked
1123            .grapheme_at(TextPosition::new(left.sub_start, 0))
1124            .expect("valid_position")
1125            .expect("grapheme");
1126        if !mask0.right.can_drop(g0.grapheme()) {
1127            return false;
1128        }
1129
1130        true
1131    }
1132
1133    /// Insert the char if it matches the cursor mask and the current section is not full.
1134    ///
1135    /// `advance_cursor()` must be called before for correct functionality.
1136    ///
1137    /// Otherwise: your mileage might vary.
1138    pub fn insert_char(&mut self, c: char) -> bool {
1139        if self.mask.is_empty() {
1140            return false;
1141        }
1142
1143        let cursor = self.masked.cursor();
1144
1145        // note: because of borrow checker. calls &mut methods.
1146        {
1147            let mask = &self.mask[cursor.x as usize];
1148            if mask.right.is_number() && self.can_insert_sign(mask, cursor.x, c) {
1149                if self.insert_sign(c) {
1150                    return true;
1151                }
1152            }
1153        }
1154        {
1155            let mask = &self.mask[cursor.x as usize];
1156            if mask.peek_left.is_number() && (mask.right.is_ltor() || mask.right.is_none()) {
1157                let left = &self.mask[cursor.x as usize - 1];
1158                if self.can_insert_sign(left, cursor.x, c) {
1159                    if self.insert_sign(c) {
1160                        return true;
1161                    }
1162                }
1163            }
1164        }
1165        {
1166            let mask = &self.mask[cursor.x as usize];
1167            if mask.right.is_rtol() {
1168                if self.insert_rtol(c) {
1169                    return true;
1170                }
1171            }
1172        }
1173        {
1174            let mask = &self.mask[cursor.x as usize];
1175            if mask.peek_left.is_rtol() && (mask.right.is_ltor() || mask.right.is_none()) {
1176                if self.insert_rtol(c) {
1177                    return true;
1178                }
1179            }
1180        }
1181        {
1182            let mask = &self.mask[cursor.x as usize];
1183            if mask.right.is_ltor() {
1184                if self.insert_ltor(c) {
1185                    #[allow(clippy::needless_return)]
1186                    return true;
1187                }
1188            }
1189        }
1190
1191        false
1192    }
1193
1194    /// Insert c into a ltor section.
1195    fn insert_ltor(&mut self, c: char) -> bool {
1196        let cursor = self.masked.cursor();
1197
1198        let mask = &self.mask[cursor.x as usize];
1199        let mask9 = &self.mask[mask.sub_end as usize - 1];
1200
1201        // overwrite digit in fraction?
1202        let g = self
1203            .masked
1204            .grapheme_at(cursor)
1205            .expect("valid_cursor")
1206            .expect("mask");
1207        if mask.right.is_fraction()
1208            && mask.right.can_overwrite_fraction(g.grapheme())
1209            && self.is_valid_char(&mask.right, c)
1210        {
1211            // to the right only defaults
1212            let frac_mask = &self.mask[cursor.x as usize + 1..mask.sub_end as usize];
1213            let frac_str = self
1214                .masked
1215                .str_slice(TextRange::new((cursor.x + 1, 0), (mask.sub_end, 0)))
1216                .expect("valid_range");
1217            if frac_str == MaskToken::empty_section(frac_mask) {
1218                self.masked.begin_undo_seq();
1219                self.masked
1220                    .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1221                    .expect("valid_cursor");
1222                self.masked.insert_char(cursor, c).expect("valid_cursor");
1223                self.masked.end_undo_seq();
1224                return true;
1225            }
1226        }
1227
1228        let g = self
1229            .masked
1230            .grapheme_at(cursor)
1231            .expect("valid_cursor")
1232            .expect("mask");
1233        if mask.right.can_overwrite(g.grapheme()) && self.is_valid_char(&mask.right, c) {
1234            if mask.right.is_separator() {
1235                self.masked.begin_undo_seq();
1236                let r = if let Some(next) = self.next_section_cursor(cursor.x) {
1237                    self.masked.set_cursor(TextPosition::new(next, 0), false)
1238                } else {
1239                    self.masked
1240                        .set_cursor(TextPosition::new(self.line_width(), 0), false)
1241                };
1242                self.masked.end_undo_seq();
1243                return r;
1244            } else if mask.right == Mask::DecimalSep {
1245                self.masked.begin_undo_seq();
1246                self.masked
1247                    .set_cursor(TextPosition::new(cursor.x + 1, 0), false);
1248                self.masked.end_undo_seq();
1249                return true;
1250            } else {
1251                self.masked.begin_undo_seq();
1252                self.masked
1253                    .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1254                    .expect("valid_cursor");
1255                self.masked.insert_char(cursor, c).expect("valid_cursor");
1256                self.masked.end_undo_seq();
1257                return true;
1258            }
1259        }
1260
1261        // can shift right
1262        let g9 = self
1263            .masked
1264            .grapheme_at(TextPosition::new(mask.sub_end - 1, 0))
1265            .expect("valid_pos")
1266            .expect("mask");
1267        if mask9.right.can_drop(g9.grapheme()) && self.is_valid_char(&mask.right, c) {
1268            self.masked.begin_undo_seq();
1269            self.masked
1270                .remove_char_range(TextRange::new((mask.sub_end - 1, 0), (mask.sub_end, 0)))
1271                .expect("valid_range");
1272            self.masked.insert_char(cursor, c).expect("valid_cursor");
1273            self.masked.end_undo_seq();
1274            return true;
1275        }
1276        false
1277    }
1278
1279    /// Insert c into a rtol section
1280    fn insert_rtol(&mut self, c: char) -> bool {
1281        let cursor = self.masked.cursor();
1282
1283        let mut mask = &self.mask[cursor.x as usize];
1284
1285        // boundary right/left. prefer right, change mask.
1286        if mask.peek_left.is_rtol() && (mask.right.is_ltor() || mask.right.is_none()) {
1287            mask = &self.mask[cursor.x as usize - 1];
1288        }
1289
1290        let mask0 = &self.mask[mask.sub_start as usize];
1291
1292        let g0 = self
1293            .masked
1294            .grapheme_at(TextPosition::new(mask.sub_start, 0))
1295            .expect("valid_pos")
1296            .expect("grapheme");
1297        if mask0.right.can_drop(g0.grapheme()) && self.is_valid_char(&mask.right, c) {
1298            self.masked.begin_undo_seq();
1299            self.masked
1300                .remove_char_range(TextRange::new((mask.sub_start, 0), (mask.sub_start + 1, 0)))
1301                .expect("valid_position");
1302            self.masked
1303                .insert_char(TextPosition::new(cursor.x - 1, 0), c)
1304                .expect("valid_position");
1305            Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1306            self.masked.end_undo_seq();
1307            return true;
1308        }
1309
1310        false
1311    }
1312
1313    /// Insert a sign c into the current number section
1314    #[allow(clippy::single_match)]
1315    fn insert_sign(&mut self, c: char) -> bool {
1316        let cursor = self.masked.cursor();
1317
1318        let mut mask = &self.mask[cursor.x as usize];
1319        // boundary right/left. prefer right, change mask.
1320        if mask.peek_left.is_number() && (mask.right.is_ltor() || mask.right.is_none()) {
1321            mask = &self.mask[cursor.x as usize - 1];
1322        }
1323
1324        // explicit sign?
1325        let idx = self.mask[mask.sec_start as usize..mask.sec_end as usize]
1326            .iter()
1327            .enumerate()
1328            .find(|(_, t)| matches!(t.right, Mask::Sign | Mask::Plus))
1329            .map(|(i, _)| mask.sec_start as usize + i);
1330
1331        // existing sign somewhere?
1332        let idx = if idx.is_none() {
1333            self.masked
1334                .graphemes(
1335                    TextRange::new((mask.sec_start, 0), (mask.sec_end, 0)),
1336                    TextPosition::new(mask.sec_start, 0),
1337                )
1338                .expect("valid_range")
1339                .enumerate()
1340                .find(|(_, g)| *g == "-" || *g == "+")
1341                .map(|(i, _)| mask.sec_start as usize + i)
1342        } else {
1343            idx
1344        };
1345
1346        let idx = if idx.is_none() {
1347            // moving sign
1348            let mut idx = mask.sec_end - 1;
1349            'f: {
1350                while idx >= mask.sec_start {
1351                    if self.mask[idx as usize].right == Mask::Numeric(EditDirection::Rtol) {
1352                        let g = self
1353                            .grapheme_at(idx)
1354                            .expect("valid_position")
1355                            .expect("grapheme");
1356
1357                        if self.mask[idx as usize].right.can_drop(g.grapheme()) {
1358                            break 'f Some(idx as usize);
1359                        }
1360                    }
1361                    idx -= 1;
1362                }
1363                None
1364            }
1365        } else {
1366            idx
1367        };
1368
1369        if let Some(idx) = idx {
1370            let mask_sign = &self.mask[idx];
1371
1372            if c == self.neg_sym() || c == '-' {
1373                // negate current
1374                let g = self
1375                    .masked
1376                    .str_slice(TextRange::new(
1377                        (idx as upos_type, 0),
1378                        (idx as upos_type + 1, 0),
1379                    ))
1380                    .expect("valid_pos")
1381                    .to_string();
1382
1383                self.masked.begin_undo_seq();
1384                self.masked
1385                    .remove_char_range(TextRange::new(
1386                        (idx as upos_type, 0),
1387                        (idx as upos_type + 1, 0),
1388                    ))
1389                    .expect("valid_range");
1390
1391                let cc = match &mask_sign.right {
1392                    Mask::Numeric(_) | Mask::Sign => {
1393                        if g == "-" {
1394                            ' '
1395                        } else {
1396                            '-'
1397                        }
1398                    }
1399                    Mask::Plus => {
1400                        if g == "-" {
1401                            '+'
1402                        } else {
1403                            '-'
1404                        }
1405                    }
1406                    _ => unreachable!(),
1407                };
1408
1409                self.masked
1410                    .insert_char(TextPosition::new(idx as upos_type, 0), cc)
1411                    .expect("valid_range");
1412                self.set_cursor(cursor.x, false);
1413                self.masked.end_undo_seq();
1414                true
1415            } else {
1416                false
1417            }
1418        } else {
1419            false
1420        }
1421    }
1422
1423    /// Remove the previous char.
1424    pub fn remove_prev(&mut self) {
1425        let cursor = self.masked.cursor();
1426
1427        if cursor.x == 0 {
1428            return;
1429        }
1430
1431        let left = &self.mask[cursor.x as usize - 1];
1432
1433        if left.right.is_rtol() {
1434            // Check if the section is empty
1435            let sec_empty = if left.right.is_rtol() {
1436                let sec_str = self
1437                    .masked
1438                    .str_slice(TextRange::new((left.sub_start, 0), (left.sub_end, 0)))
1439                    .expect("valid_range");
1440                let sec_mask = &self.mask[left.sub_start as usize..left.sub_end as usize];
1441                sec_str == MaskToken::empty_section(sec_mask)
1442            } else {
1443                false
1444            };
1445
1446            let l0 = &self.mask[left.sub_start as usize];
1447
1448            self.masked.begin_undo_seq();
1449            self.masked
1450                .remove_char_range(TextRange::new((cursor.x - 1, 0), cursor))
1451                .expect("valid_range");
1452            self.masked
1453                .insert_str(TextPosition::new(left.sub_start, 0), &l0.edit)
1454                .expect("valid_position");
1455            Self::reformat(&mut self.masked, &self.mask, left.sub_start..left.sub_end);
1456
1457            // in a rtol field keep the cursor at the same position until the
1458            // whole section is empty. Only then put it at the beginning of the section
1459            // to continue left of the section.
1460            if sec_empty {
1461                self.masked
1462                    .set_cursor(TextPosition::new(left.sub_start, 0), false);
1463            } else {
1464                // cursor stays
1465            }
1466
1467            self.masked.end_undo_seq();
1468        } else if left.right.is_ltor() {
1469            let l9 = &self.mask[left.sub_end as usize - 1];
1470
1471            self.masked.begin_undo_seq();
1472            self.masked
1473                .remove_char_range(TextRange::new((cursor.x - 1, 0), cursor))
1474                .expect("valid_range");
1475            self.masked
1476                .insert_str(TextPosition::new(left.sub_end - 1, 0), &l9.edit)
1477                .expect("valid_position");
1478
1479            Self::reformat(&mut self.masked, &self.mask, left.sub_start..left.sub_end);
1480
1481            self.masked
1482                .set_cursor(TextPosition::new(cursor.x - 1, 0), false);
1483
1484            self.masked.end_undo_seq();
1485        }
1486    }
1487
1488    /// Remove the previous char.
1489    pub fn remove_next(&mut self) {
1490        let cursor = self.masked.cursor();
1491
1492        if cursor.x as usize == self.mask.len() - 1 {
1493            return;
1494        }
1495
1496        let right = &self.mask[cursor.x as usize];
1497
1498        // remove and fill with empty
1499        if right.right.is_rtol() {
1500            let l0 = &self.mask[right.sub_start as usize];
1501
1502            self.masked.begin_undo_seq();
1503            self.masked
1504                .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1505                .expect("valid_range");
1506            self.masked
1507                .insert_str(TextPosition::new(right.sub_start, 0), &l0.edit)
1508                .expect("valid_position");
1509            Self::reformat(&mut self.masked, &self.mask, right.sub_start..right.sub_end);
1510
1511            self.masked
1512                .set_cursor(TextPosition::new(cursor.x + 1, 0), false);
1513
1514            self.masked.end_undo_seq();
1515        } else if right.right.is_ltor() {
1516            // Check if the section is empty
1517            let sec_str = self
1518                .masked
1519                .str_slice(TextRange::new((right.sub_start, 0), (right.sub_end, 0)))
1520                .expect("valid_range");
1521            let sec_mask = &self.mask[right.sub_start as usize..right.sub_end as usize];
1522            let sec_empty = sec_str == MaskToken::empty_section(sec_mask);
1523
1524            let l9 = &self.mask[right.sub_end as usize - 1];
1525
1526            self.masked.begin_undo_seq();
1527            self.masked
1528                .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1529                .expect("valid_range");
1530            self.masked
1531                .insert_str(TextPosition::new(right.sub_end - 1, 0), &l9.edit)
1532                .expect("valid_position");
1533
1534            Self::reformat(&mut self.masked, &self.mask, right.sub_start..right.sub_end);
1535
1536            // in a ltor field keep the cursor at the same position until the
1537            // whole section is empty. Only then put it at the end of the section
1538            // to continue right of the section.
1539            if sec_empty {
1540                self.masked
1541                    .set_cursor(TextPosition::new(right.sub_end, 0), false);
1542            } else {
1543                // cursor stays
1544            }
1545
1546            self.masked.end_undo_seq();
1547        }
1548    }
1549
1550    /// Remove the selection
1551    pub fn remove_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1552        // check valid range
1553        self.masked
1554            .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1555
1556        if range.is_empty() {
1557            return Ok(false);
1558        }
1559
1560        let mask = &self.mask[range.start as usize];
1561        if range.start >= mask.sub_start && range.end <= mask.sub_end {
1562            if mask.right.is_rtol() {
1563                self.masked.begin_undo_seq();
1564                self.masked
1565                    .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))
1566                    .expect("valid_range");
1567                let fill_before =
1568                    &self.mask[mask.sub_start as usize..mask.sub_start as usize + range.len()];
1569                self.masked
1570                    .insert_str(
1571                        TextPosition::new(mask.sub_start, 0),
1572                        &MaskToken::empty_section(fill_before),
1573                    )
1574                    .expect("valid_range");
1575                Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1576                self.masked.end_undo_seq();
1577            } else if mask.right.is_ltor() {
1578                self.masked.begin_undo_seq();
1579                self.masked
1580                    .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))
1581                    .expect("valid_range");
1582                let fill_after =
1583                    &self.mask[mask.sub_end as usize - range.len()..mask.sub_end as usize];
1584                self.masked
1585                    .insert_str(
1586                        TextPosition::new(mask.sub_end - range.len() as upos_type, 0),
1587                        &MaskToken::empty_section(fill_after),
1588                    )
1589                    .expect("valid_range");
1590                Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1591                self.masked.end_undo_seq();
1592            }
1593
1594            return Ok(true);
1595        }
1596
1597        let mut pos = range.start;
1598        self.masked.begin_undo_seq();
1599        loop {
1600            let mask = &self.mask[pos as usize];
1601
1602            if mask.sub_start < range.start {
1603                // partial start
1604                if mask.right.is_rtol() {
1605                    self.masked
1606                        .remove_str_range(TextRange::new((range.start, 0), (mask.sub_end, 0)))
1607                        .expect("valid_range");
1608
1609                    let len = mask.sub_end - range.start;
1610                    let fill_before =
1611                        &self.mask[mask.sub_start as usize..(mask.sub_start + len) as usize];
1612                    self.masked
1613                        .insert_str(
1614                            TextPosition::new(mask.sub_start, 0),
1615                            &MaskToken::empty_section(fill_before),
1616                        )
1617                        .expect("valid_range");
1618
1619                    Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1620
1621                    pos = mask.sub_end;
1622                } else if mask.right.is_ltor() {
1623                    self.masked
1624                        .remove_str_range(TextRange::new((range.start, 0), (mask.sub_end, 0)))
1625                        .expect("valid_range");
1626
1627                    let fill_after = &self.mask[range.start as usize..mask.sub_end as usize];
1628                    self.masked
1629                        .insert_str(
1630                            TextPosition::new(range.start, 0),
1631                            &MaskToken::empty_section(fill_after),
1632                        )
1633                        .expect("valid_range");
1634
1635                    Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1636
1637                    pos = mask.sub_end;
1638                }
1639            } else if mask.sub_end > range.end {
1640                // partial end
1641                if mask.right.is_rtol() {
1642                    self.masked
1643                        .remove_str_range(TextRange::new((mask.sub_start, 0), (range.end, 0)))
1644                        .expect("valid_range");
1645
1646                    let fill_before = &self.mask[mask.sub_start as usize..range.end as usize];
1647                    self.masked
1648                        .insert_str(
1649                            TextPosition::new(mask.sub_start, 0),
1650                            &MaskToken::empty_section(fill_before),
1651                        )
1652                        .expect("valid_range");
1653
1654                    Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1655                    pos = mask.sub_end;
1656                } else if mask.right.is_ltor() {
1657                    self.masked
1658                        .remove_str_range(TextRange::new((mask.sub_start, 0), (range.end, 0)))
1659                        .expect("valid_range");
1660
1661                    let len = range.end - mask.sub_start;
1662                    let fill_after =
1663                        &self.mask[(mask.sub_end - len) as usize..mask.sub_end as usize];
1664                    self.masked
1665                        .insert_str(
1666                            TextPosition::new(mask.sub_end - len, 0),
1667                            &MaskToken::empty_section(fill_after),
1668                        )
1669                        .expect("valid_range");
1670
1671                    pos = mask.sub_end;
1672                }
1673            } else {
1674                // full section
1675                self.masked
1676                    .remove_str_range(TextRange::new((mask.sub_start, 0), (mask.sub_end, 0)))
1677                    .expect("valid_range");
1678
1679                let sec_range = &self.mask[mask.sub_start as usize..mask.sub_end as usize];
1680                self.masked
1681                    .insert_str(
1682                        TextPosition::new(mask.sub_start, 0),
1683                        &MaskToken::empty_section(sec_range),
1684                    )
1685                    .expect("valid_range");
1686
1687                // todo: needed?: Self::reformat(&mut self.masked, &self.mask, mask.sec_start..mask.sec_end);
1688                pos = mask.sub_end;
1689            }
1690
1691            if pos >= range.end {
1692                break;
1693            }
1694        }
1695        self.masked.end_undo_seq();
1696
1697        Ok(true)
1698    }
1699
1700    /// Rebuild a section according to number-formatting.
1701    /// The main purpose is to rebuild the grouping separators.
1702    fn reformat(core: &mut TextCore<TextString>, mask: &[MaskToken], section: Range<upos_type>) {
1703        if mask[section.start as usize].right.is_rtol() {
1704            let cursor = core.cursor();
1705            let anchor = core.anchor();
1706
1707            let sec_str = core
1708                .str_slice(TextRange::new((section.start, 0), (section.end, 0)))
1709                .expect("valid_range");
1710
1711            // to be safe, always use our internal symbol set.
1712            let sym = NumberSymbols {
1713                decimal_sep: '.',
1714                decimal_grp: Some(','),
1715                negative_sym: '-',
1716                positive_sym: ' ',
1717                exponent_upper_sym: 'E',
1718                exponent_lower_sym: 'e',
1719                currency_sym: CurrencySym::new("$"),
1720            };
1721
1722            // remove all non numbers and leading 0.
1723            let mut clean = String::new();
1724            _ = clean_num(sec_str.as_ref(), &sym, &mut clean);
1725
1726            // create number format
1727            let mut tok = String::new();
1728            let submask = &mask[section.start as usize..section.end as usize];
1729            // default fmt.sym is nice
1730            for t in submask {
1731                match &t.right {
1732                    Mask::Digit0(_) => tok.push('0'),
1733                    Mask::Digit(_) => tok.push('9'),
1734                    Mask::Numeric(_) => tok.push('#'),
1735                    Mask::DecimalSep => tok.push('.'),
1736                    Mask::GroupingSep => tok.push(','),
1737                    Mask::Sign => tok.push('-'),
1738                    Mask::Plus => tok.push('+'),
1739                    Mask::Separator(s) => {
1740                        for c in s.chars() {
1741                            tok.push('\\');
1742                            tok.push(c);
1743                        }
1744                    }
1745                    Mask::None => {}
1746                    _ => unreachable!("invalid mask"),
1747                }
1748            }
1749
1750            let fmt = match NumberFormat::news(tok, sym) {
1751                Ok(v) => v,
1752                Err(_) => unreachable!("invalid mask"),
1753            };
1754            let mut out = String::new();
1755            match map_num::<_, false>(clean.as_str(), &fmt, fmt.sym(), &mut out) {
1756                Ok(_) => {}
1757                Err(_) => unreachable!("invalid mask"),
1758            }
1759
1760            core.remove_char_range(TextRange::new((section.start, 0), (section.end, 0)))
1761                .expect("valid_range");
1762            core.insert_str(TextPosition::new(section.start, 0), &out)
1763                .expect("valid_position");
1764
1765            // keep cursor intact
1766            core.set_cursor(anchor, false);
1767            core.set_cursor(cursor, true);
1768        } else if mask[section.start as usize].right.is_ltor() {
1769            let cursor = core.cursor();
1770            let anchor = core.anchor();
1771
1772            let sec_str = core
1773                .str_slice(TextRange::new((section.start, 0), (section.end, 0)))
1774                .expect("valid_range");
1775            let sec_mask = &mask[section.start as usize..section.end as usize];
1776            let mut str_new = String::new();
1777            for (g, t) in sec_str.graphemes(true).zip(sec_mask.iter()) {
1778                match t.right {
1779                    Mask::Digit0(_) | Mask::Hex0 | Mask::Oct0 | Mask::Dec0 => {
1780                        if g == " " {
1781                            str_new.push('0');
1782                        } else {
1783                            str_new.push_str(g);
1784                        }
1785                    }
1786                    _ => {
1787                        str_new.push_str(g);
1788                    }
1789                }
1790            }
1791
1792            if sec_str != str_new {
1793                core.remove_char_range(TextRange::new((section.start, 0), (section.end, 0)))
1794                    .expect("valid_range");
1795                core.insert_str(TextPosition::new(section.start, 0), &str_new)
1796                    .expect("valid_position");
1797
1798                // keep cursor intact
1799                core.set_cursor(anchor, false);
1800                core.set_cursor(cursor, true);
1801            }
1802        }
1803    }
1804}
1805
1806mod mask {
1807    use crate::upos_type;
1808    use std::fmt;
1809    use std::fmt::{Debug, Display, Formatter};
1810
1811    /// Edit direction for part of a mask.
1812    /// Numeric values can switch between right-to-left (integer part) and left-to-right (fraction).
1813    #[derive(Clone, Copy, PartialEq, Eq)]
1814    pub(super) enum EditDirection {
1815        Ltor,
1816        Rtol,
1817    }
1818
1819    /// One char of the input mask.
1820    #[allow(variant_size_differences)]
1821    #[derive(Clone, PartialEq, Eq, Default)]
1822    #[non_exhaustive]
1823    pub(super) enum Mask {
1824        // 0-9, display 0
1825        Digit0(EditDirection),
1826        // 0-9, display space
1827        Digit(EditDirection),
1828        // 0-9;sign, display space
1829        Numeric(EditDirection),
1830        DecimalSep,
1831        GroupingSep,
1832        Sign,
1833        Plus,
1834        Hex0,
1835        Hex,
1836        Oct0,
1837        Oct,
1838        Dec0,
1839        Dec,
1840        Letter,
1841        LetterOrDigit,
1842        LetterDigitSpace,
1843        AnyChar,
1844        Separator(Box<str>),
1845        #[default]
1846        None,
1847    }
1848
1849    /// One token of the input mask.
1850    ///
1851    /// Which field of the mask does this belong to:
1852    /// * Number with integer part, decimal separator, fraction and sign.
1853    /// * Consecutive mask parts of similar type.
1854    ///
1855    /// At this cursor position, what is the main mask (right) and what is possible left of
1856    /// the cursor position (peek_left).
1857    ///
1858    /// Default-values for editing and display.
1859    #[derive(Clone, PartialEq, Eq)]
1860    pub(super) struct MaskToken {
1861        pub sec_id: u16,
1862        // section/number
1863        pub sec_start: upos_type,
1864        // section/number
1865        pub sec_end: upos_type,
1866        // part of a number/section
1867        pub sub_start: upos_type,
1868        // part of a number/section
1869        pub sub_end: upos_type,
1870
1871        // token left of the cursor
1872        pub peek_left: Mask,
1873        // token right of the cursor
1874        pub right: Mask,
1875
1876        // edit-value of the token
1877        pub edit: Box<str>,
1878    }
1879
1880    impl Debug for EditDirection {
1881        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1882            write!(
1883                f,
1884                "{}",
1885                match self {
1886                    EditDirection::Ltor => ">",
1887                    EditDirection::Rtol => "<",
1888                }
1889            )
1890        }
1891    }
1892
1893    impl Display for Mask {
1894        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1895            let s = match self {
1896                Mask::Digit0(_) => "0",
1897                Mask::Digit(_) => "9",
1898                Mask::Numeric(_) => "#",
1899                Mask::DecimalSep => ".",
1900                Mask::GroupingSep => ",",
1901                Mask::Sign => "-",
1902                Mask::Plus => "+",
1903                Mask::Hex0 => "H",
1904                Mask::Hex => "h",
1905                Mask::Oct0 => "O",
1906                Mask::Oct => "o",
1907                Mask::Dec0 => "D",
1908                Mask::Dec => "d",
1909                Mask::Letter => "l",
1910                Mask::LetterOrDigit => "a",
1911                Mask::LetterDigitSpace => "c",
1912                Mask::AnyChar => "_",
1913                Mask::Separator(s) => {
1914                    write!(f, "\\")?;
1915                    s
1916                }
1917                Mask::None => "",
1918            };
1919            write!(f, "{}", s)
1920        }
1921    }
1922
1923    impl Debug for Mask {
1924        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1925            match self {
1926                Mask::Digit0(d) => {
1927                    write!(f, "{:?}0", d)
1928                }
1929                Mask::Digit(d) => {
1930                    write!(f, "{:?}9", d)
1931                }
1932                Mask::Numeric(d) => {
1933                    write!(f, "{:?}#", d)
1934                }
1935                Mask::DecimalSep => write!(f, "."),
1936                Mask::GroupingSep => write!(f, ","),
1937                Mask::Sign => write!(f, "-"),
1938                Mask::Plus => write!(f, "+"),
1939                Mask::Hex0 => write!(f, "H"),
1940                Mask::Hex => write!(f, "h"),
1941                Mask::Oct0 => write!(f, "O"),
1942                Mask::Oct => write!(f, "o"),
1943                Mask::Dec0 => write!(f, "D"),
1944                Mask::Dec => write!(f, "d"),
1945                Mask::Letter => write!(f, "l"),
1946                Mask::LetterOrDigit => write!(f, "a"),
1947                Mask::LetterDigitSpace => write!(f, "c"),
1948                Mask::AnyChar => write!(f, "_"),
1949                Mask::Separator(s) => {
1950                    write!(f, "\\")?;
1951                    write!(f, "{}", s)
1952                }
1953                Mask::None => write!(f, ""),
1954            }
1955        }
1956    }
1957
1958    impl EditDirection {
1959        pub(super) fn is_ltor(&self) -> bool {
1960            *self == EditDirection::Ltor
1961        }
1962
1963        pub(super) fn is_rtol(&self) -> bool {
1964            *self == EditDirection::Rtol
1965        }
1966    }
1967
1968    impl Mask {
1969        /// is not editable. the last field of the mask at position txt.len() can not be edited,
1970        /// but it's a valid cursor position.
1971        pub(super) fn is_none(&self) -> bool {
1972            *self == Mask::None
1973        }
1974
1975        /// left to right editing
1976        #[inline]
1977        pub(super) fn is_ltor(&self) -> bool {
1978            match self {
1979                Mask::Digit0(d) => d.is_ltor(),
1980                Mask::Digit(d) => d.is_ltor(),
1981                Mask::Numeric(d) => d.is_ltor(),
1982                Mask::GroupingSep => false,
1983                Mask::Sign => false,
1984                Mask::Plus => false,
1985                Mask::DecimalSep => true,
1986                Mask::Hex0 => true,
1987                Mask::Hex => true,
1988                Mask::Oct0 => true,
1989                Mask::Oct => true,
1990                Mask::Dec0 => true,
1991                Mask::Dec => true,
1992                Mask::Letter => true,
1993                Mask::LetterOrDigit => true,
1994                Mask::LetterDigitSpace => true,
1995                Mask::AnyChar => true,
1996                Mask::Separator(_) => true,
1997                Mask::None => false,
1998            }
1999        }
2000
2001        /// right to left editing
2002        #[inline]
2003        pub(super) fn is_rtol(&self) -> bool {
2004            match self {
2005                Mask::Digit0(d) => d.is_rtol(),
2006                Mask::Digit(d) => d.is_rtol(),
2007                Mask::Numeric(d) => d.is_rtol(),
2008                Mask::GroupingSep => true,
2009                Mask::Sign => true,
2010                Mask::Plus => true,
2011                Mask::DecimalSep => false,
2012                Mask::Hex0 => false,
2013                Mask::Hex => false,
2014                Mask::Oct0 => false,
2015                Mask::Oct => false,
2016                Mask::Dec0 => false,
2017                Mask::Dec => false,
2018                Mask::Letter => false,
2019                Mask::LetterOrDigit => false,
2020                Mask::LetterDigitSpace => false,
2021                Mask::AnyChar => false,
2022                Mask::Separator(_) => false,
2023                Mask::None => false,
2024            }
2025        }
2026
2027        /// is a number mask
2028        #[inline]
2029        pub(super) fn is_number(&self) -> bool {
2030            match self {
2031                Mask::Digit0(_)
2032                | Mask::Digit(_)
2033                | Mask::Numeric(_)
2034                | Mask::DecimalSep
2035                | Mask::GroupingSep
2036                | Mask::Sign
2037                | Mask::Plus => true,
2038                Mask::None => false,
2039                _ => false,
2040            }
2041        }
2042
2043        /// is a separator
2044        #[inline]
2045        pub(super) fn is_separator(&self) -> bool {
2046            match self {
2047                Mask::Separator(_) => true,
2048                Mask::None => false,
2049                _ => false,
2050            }
2051        }
2052
2053        #[inline]
2054        pub(super) fn is_fraction(&self) -> bool {
2055            match self {
2056                Mask::Digit0(d) | Mask::Digit(d) | Mask::Numeric(d) => d.is_ltor(),
2057                Mask::None => false,
2058                _ => false,
2059            }
2060        }
2061
2062        /// which mask-types are put together.
2063        #[inline]
2064        pub(super) fn sub_section(&self) -> u8 {
2065            match self {
2066                Mask::Digit0(_) => 0,
2067                Mask::Digit(_) => 0,
2068                Mask::Numeric(_) => 0,
2069                Mask::GroupingSep => 0,
2070
2071                Mask::Sign => 1,
2072
2073                Mask::Plus => 2,
2074
2075                Mask::DecimalSep => 3,
2076
2077                Mask::Hex0 => 4,
2078                Mask::Hex => 4,
2079
2080                Mask::Oct0 => 5,
2081                Mask::Oct => 5,
2082
2083                Mask::Dec0 => 6,
2084                Mask::Dec => 6,
2085
2086                Mask::Letter => 7,
2087                Mask::LetterOrDigit => 8,
2088                Mask::LetterDigitSpace => 9,
2089                Mask::AnyChar => 10,
2090
2091                Mask::Separator(_) => 11,
2092
2093                Mask::None => 12,
2094            }
2095        }
2096
2097        /// which mask-types constitute a number/section
2098        #[inline]
2099        pub(super) fn section(&self) -> u8 {
2100            match self {
2101                Mask::Digit0(_) => 0,
2102                Mask::Digit(_) => 0,
2103                Mask::Numeric(_) => 0,
2104                Mask::GroupingSep => 0,
2105                Mask::Sign => 0,
2106                Mask::Plus => 0,
2107                Mask::DecimalSep => 0,
2108
2109                Mask::Hex0 => 1,
2110                Mask::Hex => 1,
2111                Mask::Oct0 => 1,
2112                Mask::Oct => 1,
2113                Mask::Dec0 => 1,
2114                Mask::Dec => 1,
2115                Mask::Letter => 1,
2116                Mask::LetterOrDigit => 1,
2117                Mask::LetterDigitSpace => 1,
2118                Mask::AnyChar => 1,
2119
2120                Mask::Separator(_) => 2,
2121                Mask::None => 3,
2122            }
2123        }
2124
2125        /// mask should overwrite instead of insert
2126        #[inline]
2127        pub(super) fn can_overwrite_fraction(&self, c: &str) -> bool {
2128            match self {
2129                Mask::Digit0(_) => c == "0",
2130                Mask::Digit(_) | Mask::Numeric(_) => c == " ",
2131                Mask::DecimalSep => false,
2132                Mask::GroupingSep => false,
2133                Mask::Sign => false,
2134                Mask::Plus => false,
2135                Mask::Hex0 => false,
2136                Mask::Hex => false,
2137                Mask::Oct0 => false,
2138                Mask::Oct => false,
2139                Mask::Dec0 => false,
2140                Mask::Dec => false,
2141                Mask::Letter => false,
2142                Mask::LetterOrDigit => false,
2143                Mask::LetterDigitSpace => false,
2144                Mask::AnyChar => false,
2145                Mask::Separator(_) => false,
2146                Mask::None => false,
2147            }
2148        }
2149
2150        /// mask should overwrite instead of insert
2151        #[inline]
2152        pub(super) fn can_overwrite(&self, c: &str) -> bool {
2153            match self {
2154                Mask::Digit0(_) | Mask::Digit(_) | Mask::Numeric(_) => false,
2155                Mask::DecimalSep => "." == c,
2156                Mask::GroupingSep => false,
2157                Mask::Sign => "-" == c || " " == c,
2158                Mask::Plus => "-" == c || "+" == c || " " == c,
2159                Mask::Hex0 => c == "0",
2160                Mask::Hex => false,
2161                Mask::Oct0 => c == "0",
2162                Mask::Oct => false,
2163                Mask::Dec0 => c == "0",
2164                Mask::Dec => false,
2165                Mask::Letter => false,
2166                Mask::LetterOrDigit => false,
2167                Mask::LetterDigitSpace => false,
2168                Mask::AnyChar => false,
2169                Mask::Separator(sep) => sep.as_ref() == c,
2170                Mask::None => false,
2171            }
2172        }
2173
2174        /// char can be dropped from the text and it's ok.
2175        #[inline]
2176        pub(super) fn can_drop(&self, c: &str) -> bool {
2177            match self {
2178                Mask::Digit0(_) => c == "0",
2179                Mask::Digit(_) => c == " ",
2180                Mask::Numeric(_) => c == " ",
2181                Mask::DecimalSep => false,
2182                Mask::Sign => false,
2183                Mask::Plus => false,
2184                Mask::GroupingSep => true,
2185                Mask::Hex0 => c == "0",
2186                Mask::Hex => c == " ",
2187                Mask::Oct0 => c == "0",
2188                Mask::Oct => c == " ",
2189                Mask::Dec0 => c == "0",
2190                Mask::Dec => c == " ",
2191                Mask::Letter => c == " ",
2192                Mask::LetterOrDigit => c == " ",
2193                Mask::LetterDigitSpace => c == " ",
2194                Mask::AnyChar => c == " ",
2195                Mask::Separator(_sep) => false,
2196                Mask::None => false,
2197            }
2198        }
2199
2200        /// default char for this mask.
2201        #[inline]
2202        pub(super) fn edit_value(&self) -> &str {
2203            match self {
2204                Mask::Digit0(_) => "0",
2205                Mask::Digit(_) => " ",
2206                Mask::Numeric(_) => " ",
2207                Mask::DecimalSep => ".",
2208                Mask::GroupingSep => " ", // don't show. reformat fills it in if necessary.
2209                Mask::Sign => " ",
2210                Mask::Plus => "+",
2211                Mask::Hex0 => "0",
2212                Mask::Hex => " ",
2213                Mask::Oct0 => "0",
2214                Mask::Oct => " ",
2215                Mask::Dec0 => "0",
2216                Mask::Dec => " ",
2217                Mask::Letter => " ",
2218                Mask::LetterOrDigit => " ",
2219                Mask::LetterDigitSpace => " ",
2220                Mask::AnyChar => " ",
2221                Mask::Separator(g) => g.as_ref(),
2222                Mask::None => "",
2223            }
2224        }
2225    }
2226
2227    impl Debug for MaskToken {
2228        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2229            write!(
2230                f,
2231                "Mask #{}:{}-{} {:?} | {:?}",
2232                self.sec_id, self.sub_start, self.sub_end, self.peek_left, self.right
2233            )
2234        }
2235    }
2236
2237    impl MaskToken {
2238        /// is somewhere in the integer part of a number.
2239        #[inline]
2240        pub(super) fn is_integer_part(&self) -> bool {
2241            self.peek_left.is_rtol() || self.peek_left.is_none() && self.right.is_rtol()
2242        }
2243
2244        /// Create a string with the default edit mask.
2245        pub(super) fn empty_section(mask: &[MaskToken]) -> String {
2246            let mut buf = String::new();
2247            for m in mask {
2248                buf.push_str(&m.edit);
2249            }
2250            buf
2251        }
2252    }
2253}