Skip to main content

gpui_base/input/base/
mask_pattern.rs

1use std::borrow::Cow;
2
3use gpui::SharedString;
4
5#[derive(Clone, PartialEq, Debug)]
6pub enum MaskToken {
7    /// 0 Digit, equivalent to `[0]`
8    // Digit0,
9    /// Digit, equivalent to `[0-9]`
10    Digit,
11    /// Letter, equivalent to `[a-zA-Z]`
12    Letter,
13    /// Letter or digit, equivalent to `[a-zA-Z0-9]`
14    LetterOrDigit,
15    /// Separator
16    Sep(char),
17    /// Any character
18    Any,
19}
20
21#[allow(unused)]
22impl MaskToken {
23    /// Check if the token is any character.
24    pub fn is_any(&self) -> bool {
25        matches!(self, MaskToken::Any)
26    }
27
28    /// Check if the token is a match for the given character.
29    ///
30    /// The separator is always a match any input character.
31    fn is_match(&self, ch: char) -> bool {
32        match self {
33            MaskToken::Digit => ch.is_ascii_digit(),
34            MaskToken::Letter => ch.is_ascii_alphabetic(),
35            MaskToken::LetterOrDigit => ch.is_ascii_alphanumeric(),
36            MaskToken::Any => true,
37            MaskToken::Sep(c) => *c == ch,
38        }
39    }
40
41    /// Is the token a separator (Can be ignored)
42    fn is_sep(&self) -> bool {
43        matches!(self, MaskToken::Sep(_))
44    }
45
46    /// Check if the token is a number.
47    pub fn is_number(&self) -> bool {
48        matches!(self, MaskToken::Digit)
49    }
50
51    pub fn placeholder(&self) -> char {
52        match self {
53            MaskToken::Sep(c) => *c,
54            _ => '_',
55        }
56    }
57
58    fn mask_char(&self, ch: char) -> char {
59        match self {
60            MaskToken::Digit | MaskToken::LetterOrDigit | MaskToken::Letter => ch,
61            MaskToken::Sep(c) => *c,
62            MaskToken::Any => ch,
63        }
64    }
65
66    fn unmask_char(&self, ch: char) -> Option<char> {
67        match self {
68            MaskToken::Digit => Some(ch),
69            MaskToken::Letter => Some(ch),
70            MaskToken::LetterOrDigit => Some(ch),
71            MaskToken::Any => Some(ch),
72            _ => None,
73        }
74    }
75}
76
77#[derive(Clone, Default)]
78pub enum MaskPattern {
79    #[default]
80    None,
81    Pattern {
82        pattern: SharedString,
83        tokens: Vec<MaskToken>,
84    },
85    Number {
86        /// Group separator, e.g. "," or " "
87        separator: Option<char>,
88        /// Number of fraction digits, e.g. 2 for 123.45
89        fraction: Option<usize>,
90    },
91}
92
93impl From<&str> for MaskPattern {
94    fn from(pattern: &str) -> Self {
95        Self::new(pattern)
96    }
97}
98
99impl MaskPattern {
100    /// Create a new mask pattern
101    ///
102    /// - `9` - Digit
103    /// - `A` - Letter
104    /// - `#` - Letter or Digit
105    /// - `*` - Any character
106    /// - other characters - Separator
107    ///
108    /// For example:
109    ///
110    /// - `(999)999-9999` - US phone number: (123)456-7890
111    /// - `99999-9999` - ZIP code: 12345-6789
112    /// - `AAAA-99-####` - Custom pattern: ABCD-12-3AB4
113    /// - `*999*` - Custom pattern: (123) or [123]
114    pub fn new(pattern: &str) -> Self {
115        let tokens = pattern
116            .chars()
117            .map(|ch| match ch {
118                // '0' => MaskToken::Digit0,
119                '9' => MaskToken::Digit,
120                'A' => MaskToken::Letter,
121                '#' => MaskToken::LetterOrDigit,
122                '*' => MaskToken::Any,
123                _ => MaskToken::Sep(ch),
124            })
125            .collect();
126
127        Self::Pattern {
128            pattern: pattern.to_owned().into(),
129            tokens,
130        }
131    }
132
133    #[allow(unused)]
134    fn tokens(&self) -> Option<&Vec<MaskToken>> {
135        match self {
136            Self::Pattern { tokens, .. } => Some(tokens),
137            Self::Number { .. } => None,
138            Self::None => None,
139        }
140    }
141
142    /// Create a new mask pattern with group separator, e.g. "," or " "
143    pub fn number(sep: Option<char>) -> Self {
144        Self::Number {
145            separator: sep,
146            fraction: None,
147        }
148    }
149
150    pub fn placeholder(&self) -> Option<String> {
151        match self {
152            Self::Pattern { tokens, .. } => {
153                Some(tokens.iter().map(|token| token.placeholder()).collect())
154            }
155            Self::Number { .. } => None,
156            Self::None => None,
157        }
158    }
159
160    /// Return true if the mask pattern is None or no any pattern.
161    pub fn is_none(&self) -> bool {
162        match self {
163            Self::Pattern { tokens, .. } => tokens.is_empty(),
164            Self::Number { .. } => false,
165            Self::None => true,
166        }
167    }
168
169    /// Check is the mask text is valid.
170    ///
171    /// If the mask pattern is None, always return true.
172    pub fn is_valid(&self, mask_text: &str) -> bool {
173        if self.is_none() {
174            return true;
175        }
176
177        let mut text_index = 0;
178        let mask_text_chars: Vec<char> = mask_text.chars().collect();
179        match self {
180            Self::Pattern { tokens, .. } => {
181                for token in tokens {
182                    if text_index >= mask_text_chars.len() {
183                        break;
184                    }
185
186                    let ch = mask_text_chars[text_index];
187                    if token.is_match(ch) {
188                        text_index += 1;
189                    }
190                }
191                text_index == mask_text.len()
192            }
193            Self::Number { separator, .. } => {
194                if mask_text.is_empty() {
195                    return true;
196                }
197
198                // check if the text is valid number
199                let mut parts = mask_text.split('.');
200                let int_part = parts.next().unwrap_or("");
201                let frac_part = parts.next();
202
203                // only one dot is valid
204                if parts.next().is_some() {
205                    return false;
206                }
207
208                let sign_positions: Vec<usize> = int_part
209                    .chars()
210                    .enumerate()
211                    .filter_map(|(i, ch)| match is_sign(&ch) {
212                        true => Some(i),
213                        false => None,
214                    })
215                    .collect();
216
217                // only one sign is valid
218                // sign is only valid at the beginning of the string
219                if sign_positions.len() > 1 || sign_positions.first() > Some(&0) {
220                    return false;
221                }
222
223                // check if the integer part is valid
224                if !int_part.chars().enumerate().all(|(i, ch)| {
225                    ch.is_ascii_digit() || is_sign(&ch) && i == 0 || Some(ch) == *separator
226                }) {
227                    return false;
228                }
229
230                // check if the fraction part is valid
231                if let Some(frac) = frac_part {
232                    if !frac
233                        .chars()
234                        .all(|ch| ch.is_ascii_digit() || Some(ch) == *separator)
235                    {
236                        return false;
237                    }
238                }
239
240                true
241            }
242            Self::None => true,
243        }
244    }
245
246    /// Check if valid input char at the given position.
247    pub fn is_valid_at(&self, ch: char, pos: usize) -> bool {
248        if self.is_none() {
249            return true;
250        }
251
252        match self {
253            Self::Pattern { tokens, .. } => {
254                if let Some(token) = tokens.get(pos) {
255                    if token.is_match(ch) {
256                        return true;
257                    }
258
259                    if token.is_sep() {
260                        // If next token is match, it's valid
261                        if let Some(next_token) = tokens.get(pos + 1) {
262                            if next_token.is_match(ch) {
263                                return true;
264                            }
265                        }
266                    }
267                }
268
269                false
270            }
271            Self::Number { .. } => true,
272            Self::None => true,
273        }
274    }
275
276    /// Format the text according to the mask pattern
277    ///
278    /// For example:
279    ///
280    /// - pattern: (999)999-999
281    /// - text: 123456789
282    /// - mask_text: (123)456-789
283    pub fn mask(&self, text: &str) -> SharedString {
284        if self.is_none() {
285            return text.to_owned().into();
286        }
287
288        match self {
289            Self::Number {
290                separator,
291                fraction,
292            } => {
293                if let Some(sep) = *separator {
294                    // Remove the existing group separator
295                    let text = text.replace(sep, "");
296
297                    let mut parts = text.split('.');
298                    let int_part = parts.next().unwrap_or("");
299
300                    // Limit the fraction part to the given range, if not enough, pad with 0
301                    let frac_part = parts.next().map(|part| {
302                        part.chars()
303                            .take(fraction.unwrap_or(usize::MAX))
304                            .collect::<String>()
305                    });
306
307                    // Reverse the integer part for easier grouping
308                    let mut chars: Vec<char> = int_part.chars().rev().collect();
309
310                    // Removing the sign from formatting to avoid cases such as: -,123
311                    let maybe_signed = if let Some(pos) = chars.iter().position(is_sign) {
312                        Some(chars.remove(pos))
313                    } else {
314                        None
315                    };
316
317                    let mut result = String::new();
318                    for (i, ch) in chars.iter().enumerate() {
319                        if i > 0 && i % 3 == 0 {
320                            result.push(sep);
321                        }
322                        result.push(*ch);
323                    }
324                    let int_with_sep: String = result.chars().rev().collect();
325
326                    let final_str = if let Some(frac) = frac_part {
327                        if fraction == &Some(0) {
328                            int_with_sep
329                        } else {
330                            format!("{}.{}", int_with_sep, frac)
331                        }
332                    } else {
333                        int_with_sep
334                    };
335
336                    let final_str = if let Some(sign) = maybe_signed {
337                        format!("{}{}", sign, final_str)
338                    } else {
339                        final_str
340                    };
341
342                    return final_str.into();
343                }
344
345                text.to_owned().into()
346            }
347            Self::Pattern { tokens, .. } => {
348                let mut result = String::new();
349                let mut text_index = 0;
350                let text_chars: Vec<char> = text.chars().collect();
351                for (pos, token) in tokens.iter().enumerate() {
352                    if text_index >= text_chars.len() {
353                        break;
354                    }
355                    let ch = text_chars[text_index];
356                    // Break if expected char is not match
357                    if !token.is_sep() && !self.is_valid_at(ch, pos) {
358                        break;
359                    }
360                    let mask_ch = token.mask_char(ch);
361                    result.push(mask_ch);
362                    if ch == mask_ch {
363                        text_index += 1;
364                        continue;
365                    }
366                }
367                result.into()
368            }
369            Self::None => text.to_owned().into(),
370        }
371    }
372
373    /// Extract original text from masked text
374    pub fn unmask(&self, mask_text: &str) -> String {
375        match self {
376            Self::Number { separator, .. } => {
377                if let Some(sep) = *separator {
378                    let mut result = String::new();
379                    for ch in mask_text.chars() {
380                        if ch == sep {
381                            continue;
382                        }
383                        result.push(ch);
384                    }
385
386                    if result.contains('.') {
387                        result = result.trim_end_matches('0').to_string();
388                    }
389                    return result;
390                }
391
392                return mask_text.to_owned();
393            }
394            Self::Pattern { tokens, .. } => {
395                let mut result = String::new();
396                let mask_text_chars: Vec<char> = mask_text.chars().collect();
397                for (text_index, token) in tokens.iter().enumerate() {
398                    if text_index >= mask_text_chars.len() {
399                        break;
400                    }
401                    let ch = mask_text_chars[text_index];
402                    let unmask_ch = token.unmask_char(ch);
403                    if let Some(ch) = unmask_ch {
404                        result.push(ch);
405                    }
406                }
407                result
408            }
409            Self::None => mask_text.to_owned(),
410        }
411    }
412}
413
414#[inline]
415fn is_sign(ch: &char) -> bool {
416    matches!(ch, '+' | '-')
417}
418
419/// Normalize full-width and CJK number characters into their ASCII equivalents.
420///
421/// E.g. `123。5` -> `123.5`
422///
423/// Every mapping is 1 char to 1 char with the same UTF-16 length, so the IME
424/// marked-range offsets (in UTF-16) stay valid. The UTF-8 byte length may
425/// shrink (3 bytes to 1), the caller must use the normalized string for all
426/// byte-offset calculations.
427#[doc(hidden)]
428pub(super) fn normalize_number_input(text: &str) -> Cow<'_, str> {
429    #[inline]
430    fn normalize_char(ch: char) -> Option<char> {
431        match ch {
432            // Full-width digits 0-9
433            '\u{FF10}'..='\u{FF19}' => char::from_u32(ch as u32 - 0xFF10 + '0' as u32),
434            // Full-width plus +
435            '\u{FF0B}' => Some('+'),
436            // Full-width hyphen - and minus sign −
437            '\u{FF0D}' | '\u{2212}' => Some('-'),
438            // Full-width dot . and ideographic full stop 。
439            '\u{FF0E}' | '\u{3002}' => Some('.'),
440            // Full-width comma ,
441            '\u{FF0C}' => Some(','),
442            _ => None,
443        }
444    }
445
446    if !text.chars().any(|ch| normalize_char(ch).is_some()) {
447        return Cow::Borrowed(text);
448    }
449
450    Cow::Owned(
451        text.chars()
452            .map(|ch| normalize_char(ch).unwrap_or(ch))
453            .collect(),
454    )
455}
456
457#[cfg(test)]
458mod tests {
459    use super::{MaskPattern, MaskToken};
460
461    #[test]
462    fn test_is_match() {
463        assert_eq!(MaskToken::Sep('(').is_match('('), true);
464        assert_eq!(MaskToken::Sep('-').is_match('('), false);
465        assert_eq!(MaskToken::Sep('-').is_match('3'), false);
466
467        assert_eq!(MaskToken::Digit.is_match('0'), true);
468        assert_eq!(MaskToken::Digit.is_match('9'), true);
469        assert_eq!(MaskToken::Digit.is_match('a'), false);
470        assert_eq!(MaskToken::Digit.is_match('C'), false);
471
472        assert_eq!(MaskToken::Letter.is_match('a'), true);
473        assert_eq!(MaskToken::Letter.is_match('Z'), true);
474        assert_eq!(MaskToken::Letter.is_match('3'), false);
475        assert_eq!(MaskToken::Letter.is_match('-'), false);
476
477        assert_eq!(MaskToken::LetterOrDigit.is_match('0'), true);
478        assert_eq!(MaskToken::LetterOrDigit.is_match('9'), true);
479        assert_eq!(MaskToken::LetterOrDigit.is_match('a'), true);
480        assert_eq!(MaskToken::LetterOrDigit.is_match('Z'), true);
481        assert_eq!(MaskToken::LetterOrDigit.is_match('3'), true);
482
483        assert_eq!(MaskToken::Any.is_match('a'), true);
484        assert_eq!(MaskToken::Any.is_match('3'), true);
485        assert_eq!(MaskToken::Any.is_match('-'), true);
486        assert_eq!(MaskToken::Any.is_match(' '), true);
487    }
488
489    #[test]
490    fn test_mask_none() {
491        let mask = MaskPattern::None;
492        assert_eq!(mask.is_none(), true);
493        assert_eq!(mask.is_valid("1124124ASLDJKljk"), true);
494        assert_eq!(mask.mask("hello-world"), "hello-world");
495        assert_eq!(mask.unmask("hello-world"), "hello-world");
496    }
497
498    #[test]
499    fn test_mask_pattern1() {
500        let mask = MaskPattern::new("(AA)999-999");
501        assert_eq!(
502            mask.tokens(),
503            Some(&vec![
504                MaskToken::Sep('('),
505                MaskToken::Letter,
506                MaskToken::Letter,
507                MaskToken::Sep(')'),
508                MaskToken::Digit,
509                MaskToken::Digit,
510                MaskToken::Digit,
511                MaskToken::Sep('-'),
512                MaskToken::Digit,
513                MaskToken::Digit,
514                MaskToken::Digit,
515            ])
516        );
517
518        assert_eq!(mask.is_valid_at('(', 0), true);
519        assert_eq!(mask.is_valid_at('H', 0), true);
520        assert_eq!(mask.is_valid_at('3', 0), false);
521        assert_eq!(mask.is_valid_at('-', 0), false);
522        assert_eq!(mask.is_valid_at(')', 1), false);
523        assert_eq!(mask.is_valid_at('H', 1), true);
524        assert_eq!(mask.is_valid_at('1', 1), false);
525        assert_eq!(mask.is_valid_at('e', 2), true);
526        assert_eq!(mask.is_valid_at(')', 3), true);
527        assert_eq!(mask.is_valid_at('1', 3), true);
528        assert_eq!(mask.is_valid_at('2', 4), true);
529
530        assert_eq!(mask.is_valid("(AB)123-456"), true);
531
532        assert_eq!(mask.mask("AB123456"), "(AB)123-456");
533        assert_eq!(mask.mask("(AB)123-456"), "(AB)123-456");
534        assert_eq!(mask.mask("(AB123456"), "(AB)123-456");
535        assert_eq!(mask.mask("AB123-456"), "(AB)123-456");
536        assert_eq!(mask.mask("AB123-"), "(AB)123-");
537        assert_eq!(mask.mask("AB123--"), "(AB)123-");
538        assert_eq!(mask.mask("AB123-4"), "(AB)123-4");
539
540        let unmasked_text = mask.unmask("(AB)123-456");
541        assert_eq!(unmasked_text, "AB123456");
542
543        assert_eq!(mask.is_valid("12AB345"), false);
544        assert_eq!(mask.is_valid("(11)123-456"), false);
545        assert_eq!(mask.is_valid("##"), false);
546        assert_eq!(mask.is_valid("(AB)123456"), true);
547    }
548
549    #[test]
550    fn test_mask_pattern2() {
551        let mask = MaskPattern::new("999-999-******");
552        assert_eq!(
553            mask.tokens(),
554            Some(&vec![
555                MaskToken::Digit,
556                MaskToken::Digit,
557                MaskToken::Digit,
558                MaskToken::Sep('-'),
559                MaskToken::Digit,
560                MaskToken::Digit,
561                MaskToken::Digit,
562                MaskToken::Sep('-'),
563                MaskToken::Any,
564                MaskToken::Any,
565                MaskToken::Any,
566                MaskToken::Any,
567                MaskToken::Any,
568                MaskToken::Any,
569            ])
570        );
571
572        let text = "123456A(111)";
573        let masked_text = mask.mask(text);
574        assert_eq!(masked_text, "123-456-A(111)");
575        let unmasked_text = mask.unmask(&masked_text);
576        assert_eq!(unmasked_text, "123456A(111)");
577        assert_eq!(mask.is_valid(&masked_text), true);
578    }
579
580    #[test]
581    fn test_number_with_group_separator() {
582        // Use comma as group separator
583        let mask = MaskPattern::number(Some(','));
584        assert_eq!(mask.mask("1234567"), "1,234,567");
585        assert_eq!(mask.mask("1,234,567"), "1,234,567");
586        assert_eq!(mask.unmask("1,234,567"), "1234567");
587        let mask = MaskPattern::number(Some(','));
588        assert_eq!(mask.mask("1234567.89"), "1,234,567.89");
589        assert_eq!(mask.unmask("1,234,567.89"), "1234567.89");
590
591        // Use space as group separator
592        let mask = MaskPattern::number(Some(' '));
593        assert_eq!(mask.mask("1234567"), "1 234 567");
594        assert_eq!(mask.unmask("1 234 567"), "1234567");
595        let mask = MaskPattern::number(Some(' '));
596        assert_eq!(mask.mask("1234567.89"), "1 234 567.89");
597        assert_eq!(mask.unmask("1 234 567.89"), "1234567.89");
598
599        // No group separator
600        let mask = MaskPattern::number(None);
601        assert_eq!(mask.mask("1234567"), "1234567");
602        assert_eq!(mask.unmask("1234567"), "1234567");
603        let mask = MaskPattern::number(None);
604        assert_eq!(mask.mask("1234567.89"), "1234567.89");
605        assert_eq!(mask.unmask("1234567.89"), "1234567.89");
606    }
607
608    #[test]
609    fn test_number_with_fraction_digits() {
610        let mask = MaskPattern::Number {
611            separator: Some(','),
612            fraction: Some(4),
613        };
614
615        assert_eq!(mask.mask("1234567"), "1,234,567");
616        assert_eq!(mask.unmask("1,234,567"), "1234567");
617        assert_eq!(mask.mask("1234567."), "1,234,567.");
618        assert_eq!(mask.mask("1234567.89"), "1,234,567.89");
619        assert_eq!(mask.unmask("1,234,567.890"), "1234567.89");
620        assert_eq!(mask.mask("1234567.891"), "1,234,567.891");
621        assert_eq!(mask.mask("1234567.891234"), "1,234,567.8912");
622
623        let mask = MaskPattern::Number {
624            separator: Some(','),
625            fraction: None,
626        };
627
628        assert_eq!(mask.mask("1234567.1234567"), "1,234,567.1234567");
629
630        let mask = MaskPattern::Number {
631            separator: Some(','),
632            fraction: Some(0),
633        };
634
635        assert_eq!(mask.mask("1234567.1234567"), "1,234,567");
636    }
637
638    #[test]
639    fn test_signed_number_numbers() {
640        let mask = MaskPattern::Number {
641            separator: Some(','),
642            fraction: Some(2),
643        };
644
645        assert_eq!(mask.is_valid("-"), true);
646        assert_eq!(mask.is_valid("-1234567"), true);
647        assert_eq!(mask.is_valid("-1,234,567"), true);
648        assert_eq!(mask.is_valid("-1234567."), true);
649        assert_eq!(mask.is_valid("-1234567.89"), true);
650
651        assert_eq!(mask.is_valid("+"), true);
652        assert_eq!(mask.is_valid("+1234567"), true);
653        assert_eq!(mask.is_valid("+1,234,567"), true);
654        assert_eq!(mask.is_valid("+1234567."), true);
655        assert_eq!(mask.is_valid("+1234567.89"), true);
656
657        // Only one sign is valid
658        assert_eq!(mask.is_valid("+-"), false);
659        assert_eq!(mask.is_valid("-+"), false);
660        assert_eq!(mask.is_valid("+-1234567"), false);
661
662        // No sign is valid in the middle of the number
663        assert_eq!(mask.is_valid("1,-234,567"), false);
664        assert_eq!(mask.is_valid("12-34567.89"), false);
665
666        // Signs in fractions are invalid
667        assert_eq!(mask.is_valid("+1234567.-"), false);
668
669        // The separator does not show up before the sign i.e. -,123
670        assert_eq!(mask.mask("-123"), "-123");
671
672        assert_eq!(mask.mask("-1234567"), "-1,234,567");
673        assert_eq!(mask.mask("+1234567"), "+1,234,567");
674        assert_eq!(mask.unmask("-1,234,567"), "-1234567");
675        assert_eq!(mask.mask("-1234567."), "-1,234,567.");
676        assert_eq!(mask.mask("-1234567.89"), "-1,234,567.89");
677    }
678
679    #[test]
680    fn test_number_leading_dot() {
681        let mask = MaskPattern::number(None);
682        assert_eq!(mask.is_valid("."), true);
683        assert_eq!(mask.is_valid(".5"), true);
684        assert_eq!(mask.is_valid("-."), true);
685        assert_eq!(mask.is_valid("-.5"), true);
686        assert_eq!(mask.is_valid("1.2.3"), false);
687        assert_eq!(mask.is_valid("1.."), false);
688
689        // A bare leading dot is kept as-is (not completed to "0."), so that
690        // deleting the integer part of "1.2" keeps ".2" and stays editable.
691        assert_eq!(mask.mask("."), ".");
692        assert_eq!(mask.mask(".5"), ".5");
693        assert_eq!(mask.mask("-.5"), "-.5");
694        assert_eq!(mask.mask("+.5"), "+.5");
695
696        let mask = MaskPattern::number(Some(','));
697        assert_eq!(mask.mask(".5"), ".5");
698        assert_eq!(mask.mask("-.5"), "-.5");
699    }
700
701    #[test]
702    fn test_normalize_number_input() {
703        use std::borrow::Cow;
704
705        use super::normalize_number_input;
706
707        // Fast path: no allocation when nothing to normalize
708        assert!(matches!(
709            normalize_number_input("-1,234.5"),
710            Cow::Borrowed(_)
711        ));
712
713        // Full-width digits
714        assert_eq!(normalize_number_input("0123456789"), "0123456789");
715        // Full-width signs, dot and comma
716        assert_eq!(normalize_number_input("+1.5"), "+1.5");
717        assert_eq!(normalize_number_input("-1,234"), "-1,234");
718        // Minus sign (U+2212)
719        assert_eq!(normalize_number_input("−1.5"), "-1.5");
720        // Ideographic full stop
721        assert_eq!(normalize_number_input("12。5"), "12.5");
722        // Other characters are kept as-is
723        assert_eq!(normalize_number_input("ab 中 1"), "ab 中 1");
724    }
725}