def _apply_meta_attach(meta, ctx): action = ctx.new_action() begin = meta.startswith(META_ATTACH_FLAG) end = meta.endswith(META_ATTACH_FLAG) if begin: meta = meta[len(META_ATTACH_FLAG):] action.prev_attach = True if end: meta = meta[:-len(META_ATTACH_FLAG)] action.next_attach = True last_word = ctx.last_action.word or '' if not meta: # We use an empty connection to indicate a "break" in the # application of orthography rules. This allows the # stenographer to tell Plover not to auto-correct a word. action.orthography = False elif (last_word and not meta.isspace() and ctx.last_action.orthography and begin and (not end or _has_word_boundary(meta))): new_word = add_suffix(last_word, meta) common_len = len(commonprefix([last_word, new_word])) replaced = last_word[common_len:] action.prev_replace = ctx.last_text(len(replaced)) assert replaced.lower() == action.prev_replace.lower() last_word = last_word[:common_len] meta = new_word[common_len:] action.text = meta if action.prev_attach: action.word = _rightmost_word(last_word + meta) return action
def _apply_meta_attach(meta, ctx): action = ctx.new_action() begin = meta.startswith(META_ATTACH_FLAG) end = meta.endswith(META_ATTACH_FLAG) if begin: meta = meta[len(META_ATTACH_FLAG):] action.prev_attach = True if end: meta = meta[:-len(META_ATTACH_FLAG)] action.next_attach = True last_word = ctx.last_action.word or '' if not meta: # We use an empty connection to indicate a "break" in the # application of orthography rules. This allows the # stenographer to tell Plover not to auto-correct a word. action.orthography = False elif ( last_word and not meta.isspace() and ctx.last_action.orthography and begin and (not end or _has_word_boundary(meta)) ): new_word = add_suffix(last_word, meta) common_len = len(commonprefix([last_word, new_word])) replaced = last_word[common_len:] action.prev_replace = ctx.last_text(len(replaced)) assert replaced.lower() == action.prev_replace.lower() last_word = last_word[:common_len] meta = new_word[common_len:] action.text = meta if action.prev_attach: action.word = _rightmost_word(last_word + meta) return action
def test_runner(test, *args, **kwargs): fn(test, *args, **kwargs) result = add_suffix(test.word, test.suffix) report = orthographic_rules_report(test.word, test.suffix) msg = '%s\n%s' % ( test.format(location=False, result=result), '\n'.join('• %s: %s' % (k, pformat(v)) for k, v in report.items()), ) assert result == test.expected, msg
def _atom_to_action_spaces_after(atom, last_action): """Convert an atom into an action. Arguments: atom -- A string holding an atom. An atom is an irreducible string that is either entirely a single meta command or entirely text containing no meta commands. last_action -- The context in which the new action takes place. Returns: An action for the atom. """ action = _Action(space_char=last_action.space_char, case=last_action.case) last_word = last_action.word last_glue = last_action.glue last_attach = last_action.attach last_capitalize = last_action.capitalize last_lower = last_action.lower last_upper = last_action.upper last_upper_carry = last_action.upper_carry last_orthography = last_action.orthography last_space = SPACE if last_action.text.endswith(SPACE) else NO_SPACE was_space = len(last_space) is not 0 begin = False # for meta attach meta = _get_meta(atom) if meta is not None: meta = _unescape_atom(meta) if meta in META_COMMAS: action.text = meta + SPACE if last_action.text != '': if was_space: action.replace = SPACE else: action.replace = NO_SPACE if last_attach: action.replace = NO_SPACE elif meta in META_STOPS: action.text = meta + SPACE action.capitalize = True action.lower = False if last_action.text != '': if was_space: action.replace = SPACE else: action.replace = NO_SPACE if last_attach: action.replace = NO_SPACE elif meta == META_CAPITALIZE: action = last_action.copy_state() action.capitalize = True action.lower = False elif meta == META_LOWER: action = last_action.copy_state() if was_space: # Persist space state action.replace = SPACE action.text = SPACE action.lower = True action.capitalize = False elif meta == META_UPPER: action = last_action.copy_state() action.lower = False action.upper = True action.capitalize = False elif (meta.startswith(META_CARRY_CAPITALIZATION) or meta.startswith(META_ATTACH_FLAG + META_CARRY_CAPITALIZATION)): action = _apply_carry_capitalize(meta, last_action, spaces_after=True) elif meta == META_RETRO_CAPITALIZE: action = last_action.copy_state() action.word = _capitalize(action.word) if len(last_action.text) < len(last_action.word): action.replace = last_action.word + SPACE action.text = _capitalize(last_action.word + SPACE) else: action.replace = last_action.text action.text = _capitalize_nowhitespace(last_action.text) elif meta == META_RETRO_LOWER: action = last_action.copy_state() action.word = _lower(action.word) if len(last_action.text) < len(last_action.word): action.replace = last_action.word + SPACE action.text = _lower(last_action.word + SPACE) else: action.replace = last_action.text action.text = _lower_nowhitespace(last_action.text) elif meta == META_RETRO_UPPER: action = last_action.copy_state() action.word = _upper(action.word) action.upper_carry = True if len(last_action.text) < len(last_action.word): action.replace = last_action.word + SPACE action.text = _upper(last_action.word + SPACE) else: action.replace = last_action.text action.text = _upper(last_action.text) elif meta.startswith(META_RETRO_FORMAT): if meta.startswith(META_RETRO_FORMAT) and meta.endswith(')'): action = _apply_currency(meta, last_action, spaces_after=True) elif meta.startswith(META_COMMAND): action = last_action.copy_state() action.command = meta[len(META_COMMAND):] elif meta.startswith(META_MODE): action = last_action.copy_state() action = _change_mode(meta[len(META_MODE):], action) elif meta.startswith(META_GLUE_FLAG): action.glue = True text = meta[len(META_GLUE_FLAG):] if last_capitalize: text = _capitalize(text) if last_lower: text = _lower(text) action.text = text + SPACE action.word = _rightmost_word(text) if last_glue: if was_space: action.replace = SPACE else: action.replace = NO_SPACE action.word = _rightmost_word(last_word + text) if last_attach: action.replace = NO_SPACE action.word = _rightmost_word(last_word + text) elif (meta.startswith(META_ATTACH_FLAG) or meta.endswith(META_ATTACH_FLAG)): begin = meta.startswith(META_ATTACH_FLAG) end = meta.endswith(META_ATTACH_FLAG) if begin: meta = meta[len(META_ATTACH_FLAG):] if end and len(meta) >= len(META_ATTACH_FLAG): meta = meta[:-len(META_ATTACH_FLAG)] space = NO_SPACE if end else SPACE replace_space = NO_SPACE if last_attach else SPACE if end: action.attach = True if begin and end and meta == '': # We use an empty connection to indicate a "break" in the # application of orthography rules. This allows the # stenographer to tell plover not to auto-correct a word. action.orthography = False if last_action.text != '': action.replace = replace_space if (((begin and not end) or (begin and end and ' ' in meta)) and last_orthography): new = orthography.add_suffix(last_word.lower(), meta) common = commonprefix([last_word.lower(), new]) if last_action.text == '': replace_space = NO_SPACE action.replace = last_word[len(common):] + replace_space meta = new[len(common):] if begin and end: if last_action.text != '': action.replace = replace_space if last_capitalize: meta = _capitalize(meta) if last_lower: meta = _lower(meta) if last_upper_carry: meta = _upper(meta) action.upper_carry = True action.text = meta + space action.word = _rightmost_word( last_word[:len(last_word + last_space)-len(action.replace)] + meta) if end and not begin and last_space == SPACE: action.word = _rightmost_word(meta) elif meta.startswith(META_KEY_COMBINATION): action = last_action.copy_state() action.combo = meta[len(META_KEY_COMBINATION):] else: text = _unescape_atom(atom) if last_capitalize: text = _capitalize(text) if last_lower: text = _lower(text) if last_upper: text = _upper(text) action.upper_carry = True action.text = text + SPACE action.word = _rightmost_word(text) action.text = _apply_mode(action.text, action.case, action.space_char, begin, last_attach, last_glue, last_capitalize, last_upper, last_lower) return action
def test_add_suffix(self): cases = ( ('artistic', 'ly', 'artistically'), ('cosmetic', 'ly', 'cosmetically'), ('establish', 's', 'establishes'), ('speech', 's', 'speeches'), ('approach', 's', 'approaches'), ('beach', 's', 'beaches'), ('arch', 's', 'arches'), ('larch', 's', 'larches'), ('march', 's', 'marches'), ('search', 's', 'searches'), ('starch', 's', 'starches'), ('stomach', 's', 'stomachs'), ('monarch', 's', 'monarchs'), ('patriarch', 's', 'patriarchs'), ('oligarch', 's', 'oligarchs'), ('cherry', 's', 'cherries'), ('day', 's', 'days'), ('penny', 's', 'pennies'), ('pharmacy', 'ist', 'pharmacist'), ('melody', 'ist', 'melodist'), ('pacify', 'ist', 'pacifist'), ('geology', 'ist', 'geologist'), ('metallurgy', 'ist', 'metallurgist'), ('anarchy', 'ist', 'anarchist'), ('monopoly', 'ist', 'monopolist'), ('alchemy', 'ist', 'alchemist'), ('botany', 'ist', 'botanist'), ('therapy', 'ist', 'therapist'), ('theory', 'ist', 'theorist'), ('psychiatry', 'ist', 'psychiatrist'), ('lobby', 'ist', 'lobbyist'), ('hobby', 'ist', 'hobbyist'), ('copy', 'ist', 'copyist'), ('beauty', 'ful', 'beautiful'), ('weary', 'ness', 'weariness'), ('weary', 'some', 'wearisome'), ('lonely', 'ness', 'loneliness'), ('narrate', 'ing', 'narrating'), ('narrate', 'or', 'narrator'), ('generalize', 'ability', 'generalizability'), ('reproduce', 'able', 'reproducible'), ('grade', 'ations', 'gradations'), ('urine', 'ary', 'urinary'), ('achieve', 'able', 'achievable'), ('polarize', 'ation', 'polarization'), ('done', 'or', 'donor'), ('analyze', 'ed', 'analyzed'), ('narrate', 'ing', 'narrating'), ('believe', 'able', 'believable'), ('animate', 'ors', 'animators'), ('discontinue', 'ation', 'discontinuation'), ('innovate', 'ive', 'innovative'), ('future', 'ists', 'futurists'), ('illustrate', 'or', 'illustrator'), ('emerge', 'ent', 'emergent'), ('equip', 'ed', 'equipped'), ('defer', 'ed', 'deferred'), ('defer', 'er', 'deferrer'), ('defer', 'ing', 'deferring'), ('pigment', 'ed', 'pigmented'), ('refer', 'ed', 'referred'), ('fix', 'ed', 'fixed'), ('alter', 'ed', 'altered'), ('interpret', 'ing', 'interpreting'), ('wonder', 'ing', 'wondering'), ('target', 'ing', 'targeting'), ('limit', 'er', 'limiter'), ('maneuver', 'ing', 'maneuvering'), ('monitor', 'ing', 'monitoring'), ('color', 'ing', 'coloring'), ('inhibit', 'ing', 'inhibiting'), ('master', 'ed', 'mastered'), ('target', 'ing', 'targeting'), ('fix', 'ed', 'fixed'), ('scrap', 'y', 'scrappy'), ('trip', 's', 'trips'), ('equip', 's', 'equips'), ('bat', 'en', 'batten'), ('smite', 'en', 'smitten'), ('got', 'en', 'gotten'), ('bite', 'en', 'bitten'), ('write', 'en', 'written'), ('flax', 'en', 'flaxen'), ('wax', 'en', 'waxen'), ('fast', 'est', 'fastest'), ('white', 'er', 'whiter'), ('crap', 'y', 'crappy'), ('lad', 'er', 'ladder'), ('translucent', 'cy', 'translucency'), ('bankrupt', 'cy', 'bankruptcy'), ('inadequate', 'cy', 'inadequacy'), ('secret', 'cy', 'secrecy'), ('impolite', 'cy', 'impolicy'), ('idiot', 'cy', 'idiocy'), ('free', 'ed', 'freed'), ('free', 'er', 'freer'), ('regulate', 'ry', 'regulatory'), ) for word, suffix, expected in cases: result = add_suffix(word, suffix) msg = 'add_suffix(%r, %r) returned %r instead of %r' % ( word, suffix, result, expected, ) self.assertEqual(result, expected, msg=msg)
def _atom_to_action_spaces_before(atom, last_action): """Convert an atom into an action. Arguments: atom -- A string holding an atom. An atom is an irreducible string that is either entirely a single meta command or entirely text containing no meta commands. last_action -- The context in which the new action takes place. Returns: An action for the atom. """ action = _Action() last_word = last_action.word last_glue = last_action.glue last_attach = last_action.attach last_capitalize = last_action.capitalize last_lower = last_action.lower last_upper = last_action.upper last_upper_carry = last_action.upper_carry last_orthography = last_action.orthography meta = _get_meta(atom) if meta is not None: meta = _unescape_atom(meta) if meta in META_COMMAS: action.text = meta elif meta in META_STOPS: action.text = meta action.capitalize = True action.lower = False action.upper = False elif meta == META_CAPITALIZE: action = last_action.copy_state() action.capitalize = True action.lower = False action.upper = False elif meta == META_LOWER: action = last_action.copy_state() action.lower = True action.upper = False action.capitalize = False elif meta == META_UPPER: action = last_action.copy_state() action.lower = False action.upper = True action.capitalize = False elif meta == META_RETRO_CAPITALIZE: action = last_action.copy_state() action.word = _capitalize(action.word) if len(last_action.text) < len(last_action.word): action.replace = last_action.word action.text = _capitalize(last_action.word) else: action.replace = last_action.text action.text = _capitalize_nowhitespace(last_action.text) elif meta == META_RETRO_LOWER: action = last_action.copy_state() action.word = _lower(action.word) if len(last_action.text) < len(last_action.word): action.replace = last_action.word action.text = _lower(last_action.word) else: action.replace = last_action.text action.text = _lower_nowhitespace(last_action.text) elif meta == META_RETRO_UPPER: action = last_action.copy_state() action.word = _upper(action.word) action.upper_carry = True if len(last_action.text) < len(last_action.word): action.replace = last_action.word action.text = _upper(last_action.word) else: action.replace = last_action.text action.text = _upper(last_action.text) elif meta.startswith(META_RETRO_FORMAT): if (meta.startswith(META_RETRO_FORMAT) and meta.endswith(')')): dict_format = meta[len(META_RETRO_FORMAT):-len(')')] action = last_action.copy_state() action.replace = last_action.word try: float(last_action.word) except ValueError: pass else: format = dict_format.replace('c', '{:,.2f}') cast_input = float(last_action.word) try: int(last_action.word) except ValueError: pass else: format = dict_format.replace('c', '{:,}') cast_input = int(last_action.word) action.text = format.format(cast_input) action.word = action.text elif meta.startswith(META_COMMAND): action = last_action.copy_state() action.command = meta[len(META_COMMAND):] elif meta.startswith(META_GLUE_FLAG): action.glue = True glue = last_glue or last_attach space = NO_SPACE if glue else SPACE text = meta[len(META_GLUE_FLAG):] if last_capitalize: text = _capitalize(text) if last_lower: text = _lower(text) action.text = space + text action.word = _rightmost_word(last_word + action.text) elif (meta.startswith(META_ATTACH_FLAG) or meta.endswith(META_ATTACH_FLAG)): begin = meta.startswith(META_ATTACH_FLAG) end = meta.endswith(META_ATTACH_FLAG) if begin: meta = meta[len(META_ATTACH_FLAG):] if end and len(meta) >= len(META_ATTACH_FLAG): meta = meta[:-len(META_ATTACH_FLAG)] space = NO_SPACE if begin or last_attach else SPACE if end: action.attach = True if begin and end and meta == '': # We use an empty connection to indicate a "break" in the # application of orthography rules. This allows the stenographer # to tell plover not to auto-correct a word. action.orthography = False if (((begin and not end) or (begin and end and ' ' in meta)) and last_orthography): new = orthography.add_suffix(last_word.lower(), meta) common = commonprefix([last_word.lower(), new]) action.replace = last_word[len(common):] meta = new[len(common):] if last_capitalize: meta = _capitalize(meta) if last_lower: meta = _lower(meta) if last_upper_carry: meta = _upper(meta) action.upper_carry = True action.text = space + meta action.word = _rightmost_word( last_word[:len(last_word)-len(action.replace)] + action.text) elif meta.startswith(META_KEY_COMBINATION): action = last_action.copy_state() action.combo = meta[len(META_KEY_COMBINATION):] else: text = _unescape_atom(atom) if last_capitalize: text = _capitalize(text) if last_lower: text = _lower(text) if last_upper: text = _upper(text) action.upper_carry = True space = NO_SPACE if last_attach else SPACE action.text = space + text action.word = _rightmost_word(text) return action
def test_add_suffix(self): cases = ( ('artistic', 'ly', 'artistically'), ('cosmetic', 'ly', 'cosmetically'), ('establish', 's', 'establishes'), ('speech', 's', 'speeches'), ('approach', 's', 'approaches'), ('beach', 's', 'beaches'), ('arch', 's', 'arches'), ('larch', 's', 'larches'), ('march', 's', 'marches'), ('search', 's', 'searches'), ('starch', 's', 'starches'), ('stomach', 's', 'stomachs'), ('monarch', 's', 'monarchs'), ('patriarch', 's', 'patriarchs'), ('oligarch', 's', 'oligarchs'), ('cherry', 's', 'cherries'), ('day', 's', 'days'), ('penny', 's', 'pennies'), ('pharmacy', 'ist', 'pharmacist'), ('melody', 'ist', 'melodist'), ('pacify', 'ist', 'pacifist'), ('geology', 'ist', 'geologist'), ('metallurgy', 'ist', 'metallurgist'), ('anarchy', 'ist', 'anarchist'), ('monopoly', 'ist', 'monopolist'), ('alchemy', 'ist', 'alchemist'), ('botany', 'ist', 'botanist'), ('therapy', 'ist', 'therapist'), ('theory', 'ist', 'theorist'), ('psychiatry', 'ist', 'psychiatrist'), ('lobby', 'ist', 'lobbyist'), ('hobby', 'ist', 'hobbyist'), ('copy', 'ist', 'copyist'), ('beauty', 'ful', 'beautiful'), ('weary', 'ness', 'weariness'), ('weary', 'some', 'wearisome'), ('lonely', 'ness', 'loneliness'), ('narrate', 'ing', 'narrating'), ('narrate', 'or', 'narrator'), ('generalize', 'ability', 'generalizability'), ('reproduce', 'able', 'reproducible'), ('grade', 'ations', 'gradations'), ('urine', 'ary', 'urinary'), ('achieve', 'able', 'achievable'), ('polarize', 'ation', 'polarization'), ('done', 'or', 'donor'), ('analyze', 'ed', 'analyzed'), ('narrate', 'ing', 'narrating'), ('believe', 'able', 'believable'), ('animate', 'ors', 'animators'), ('discontinue', 'ation', 'discontinuation'), ('innovate', 'ive', 'innovative'), ('future', 'ists', 'futurists'), ('illustrate', 'or', 'illustrator'), ('emerge', 'ent', 'emergent'), ('equip', 'ed', 'equipped'), ('defer', 'ed', 'deferred'), ('defer', 'er', 'deferrer'), ('defer', 'ing', 'deferring'), ('pigment', 'ed', 'pigmented'), ('refer', 'ed', 'referred'), ('fix', 'ed', 'fixed'), ('alter', 'ed', 'altered'), ('interpret', 'ing', 'interpreting'), ('wonder', 'ing', 'wondering'), ('target', 'ing', 'targeting'), ('limit', 'er', 'limiter'), ('maneuver', 'ing', 'maneuvering'), ('monitor', 'ing', 'monitoring'), ('color', 'ing', 'coloring'), ('inhibit', 'ing', 'inhibiting'), ('master', 'ed', 'mastered'), ('target', 'ing', 'targeting'), ('fix', 'ed', 'fixed'), ('scrap', 'y', 'scrappy'), ('trip', 's', 'trips'), ('equip', 's', 'equips'), ('bat', 'en', 'batten'), ('smite', 'en', 'smitten'), ('got', 'en', 'gotten'), ('bite', 'en', 'bitten'), ('write', 'en', 'written'), ('flax', 'en', 'flaxen'), ('wax', 'en', 'waxen'), ('fast', 'est', 'fastest'), ('white', 'er', 'whiter'), ('crap', 'y', 'crappy'), ('lad', 'er', 'ladder'), ('translucent', 'cy', 'translucency'), ('bankrupt', 'cy', 'bankruptcy'), ('inadequate', 'cy', 'inadequacy'), ('secret', 'cy', 'secrecy'), ('impolite', 'cy', 'impolicy'), ('idiot', 'cy', 'idiocy'), ('free', 'ed', 'freed'), ('free', 'er', 'freer'), ('regulate', 'ry', 'regulatory'), ) failed = [] for word, suffix, expected in cases: if add_suffix(word, suffix) != expected: failed.append((word, suffix, expected)) for word, suffix, expected in failed: print 'add_suffix(%s, %s) is %s not %s' % ( word, suffix, add_suffix(word, suffix), expected) self.assertEqual(len(failed), 0)
def test_add_suffix(word, suffix, expected): assert add_suffix(word, suffix) == expected