diff --git a/generate_espanso.py b/generate_espanso.py index 4e35df6..b8deebd 100755 --- a/generate_espanso.py +++ b/generate_espanso.py @@ -177,17 +177,19 @@ def generate_pack(words, title, desc, include_accents=True): for word in sorted(set(words)): if len(word) < 3: continue - for typo in sorted(generate_all_typos(word, include_accents)): - if typo in seen: - continue - seen.add(typo) - lines.append(f" - trigger: {esc(typo)}") - lines.append(f" replace: {esc(word)}") - if not any(c in word for c in 'àèéìòù'): - lines.append(" propagate_case: true") - lines.append(" word: true") - lines.append("") - total += 1 + typos = sorted(t for t in generate_all_typos(word, include_accents) if t not in seen) + if not typos: + continue + seen.update(typos) + lines.append(" - triggers:") + for typo in typos: + lines.append(f' - {esc(typo)}') + lines.append(f" replace: {esc(word)}") + if not any(c in word for c in 'àèéìòù'): + lines.append(" propagate_case: true") + lines.append(" word: true") + lines.append("") + total += len(typos) return '\n'.join(lines), total @@ -196,20 +198,23 @@ def generate_accenti_pack(): ACCENTI_WORDS, "Accenti", "Missing accents, future tense verbs, -ità nouns" ) + short_groups = [ + (["e'", "e1"], "è"), + (["si'", "si1"], "sì"), + (["la'", "la1"], "là"), + (["li'", "li1"], "lì"), + ] short_lines = [ " # Short accented words (too short for the auto-generator)", ] - for trigger, replace in [ - ("e'", "è"), ("e1", "è"), - ("si'", "sì"), ("si1", "sì"), - ("la'", "là"), ("la1", "là"), - ("li'", "lì"), ("li1", "lì"), - ]: - short_lines.append(f" - trigger: {esc(trigger)}") + for triggers, replace in short_groups: + short_lines.append(" - triggers:") + for t in triggers: + short_lines.append(f' - {esc(t)}') short_lines.append(f" replace: {esc(replace)}") short_lines.append(" word: true") short_lines.append("") - total += 1 + total += len(triggers) return content + '\n' + '\n'.join(short_lines), total @@ -350,7 +355,7 @@ def get_version() -> str: "refuos-italiano": { "title": "Refuos Italiano", "description": "Real-time autocorrection for everyday Italian words. Fixes typos like acnhe→anche, comunqeu→comunque.", - "tags": ["italian", "autocorrect", "typo", "italiano"], + "tags": ["italian", "autocorrect", "typo", "italiano", "languages", "spell-correction", "typofixer"], "readme": textwrap.dedent("""\ # Refuos Italiano @@ -364,7 +369,7 @@ def get_version() -> str: |------------|--------------| | `acnhe` | `anche` | | `comunqeu` | `comunque` | - | `perche` | `perché` | + | `probelma` | `problema` | ~2,500 rules in total. @@ -376,7 +381,7 @@ def get_version() -> str: "refuos-accenti": { "title": "Refuos Accenti", "description": "Autocorrection for Italian accents, future-tense verbs and -ità nouns. Fixes perche→perché, aggiungero→aggiungerò.", - "tags": ["italian", "autocorrect", "accents", "accenti", "italiano"], + "tags": ["italian", "autocorrect", "accents", "accenti", "italiano", "languages", "spell-correction", "typofixer"], "readme": textwrap.dedent("""\ # Refuos Accenti @@ -402,7 +407,7 @@ def get_version() -> str: "refuos-dev": { "title": "Refuos Dev", "description": "Autocorrection for tech and code terms: HTML, CSS, patterns, paradigms, Git, DevOps, Python and more. Fixes cosnt→const, reutrn→return.", - "tags": ["dev", "autocorrect", "code", "html", "css", "git", "devops", "python", "patterns", "architecture"], + "tags": ["dev", "autocorrect", "code", "html", "css", "git", "devops", "python", "patterns", "architecture", "spell-correction", "typofixer"], "readme": textwrap.dedent("""\ # Refuos Dev @@ -425,7 +430,8 @@ def get_version() -> str: Framework-agnostic: works for JavaScript, TypeScript, Python, Go, Java, and any other stack. For framework-specific terms (React hooks, Vue Composition API, NestJS decorators, Django ORM, etc.) use a local - dictionary — see `dictionaries/local/README.md`. + dictionary — see the [project repository](https://github.com/heavybeard/refuos) + for documentation on local dictionaries. ## Source diff --git a/tests/test_yaml_generation.py b/tests/test_yaml_generation.py index 118df75..0be7785 100644 --- a/tests/test_yaml_generation.py +++ b/tests/test_yaml_generation.py @@ -37,12 +37,12 @@ def test_matches_is_non_empty_list(self): assert len(doc["matches"]) > 0 assert total > 0 - def test_every_match_has_trigger_replace_word(self): + def test_every_match_has_triggers_replace_word(self): content, _ = ge.generate_pack(["anche", "bello"], "Test", "Test pack") doc = _parse(content) for match in doc["matches"]: - # every match must have a trigger - assert "trigger" in match, f"Match missing trigger: {match}" + assert "triggers" in match, f"Match missing triggers: {match}" + assert isinstance(match["triggers"], list), f"triggers should be a list: {match}" assert "replace" in match, f"Match missing replace: {match}" assert match.get("word") is True, f"Match missing word:true: {match}" @@ -77,11 +77,12 @@ def test_short_words_below_3_chars_are_skipped(self): content, total = ge.generate_pack(["ab"], "Test", "Short words") assert total == 0 - def test_total_matches_actual_rules_count(self): + def test_total_matches_actual_trigger_count(self): words = ["anche", "bello", "comunque"] content, total = ge.generate_pack(words, "Test", "Count check") doc = _parse(content) - assert len(doc["matches"]) == total + trigger_count = sum(len(m["triggers"]) for m in doc["matches"]) + assert trigger_count == total # --------------------------------------------------------------------------- @@ -101,9 +102,9 @@ def test_no_regex_rules(self): content, _ = ge.generate_accenti_pack() assert "regex:" not in content - def test_contains_trigger_rules(self): + def test_contains_triggers_rules(self): content, _ = ge.generate_accenti_pack() - assert "trigger:" in content + assert "triggers:" in content def test_hardcoded_short_word_rules_present(self): content, _ = ge.generate_accenti_pack() @@ -133,11 +134,11 @@ def test_no_accent_variants_in_triggers(self): content, _ = ge.generate_dev_pack() doc = _parse(content) for match in doc["matches"]: - trigger = match.get("trigger", "") - for ch in ACCENTED_CHARS: - assert ch not in trigger, ( - f"Accented char '{ch}' found in dev trigger '{trigger}'" - ) + for trigger in match.get("triggers", []): + for ch in ACCENTED_CHARS: + assert ch not in trigger, ( + f"Accented char '{ch}' found in dev trigger '{trigger}'" + ) # --------------------------------------------------------------------------- @@ -167,8 +168,8 @@ def test_no_accent_variants_in_triggers(self): content, _ = ge.generate_local_pack("local", ["playwright", "turborepo"]) doc = _parse(content) for match in doc.get("matches") or []: - trigger = match.get("trigger", "") - for ch in ACCENTED_CHARS: - assert ch not in trigger, ( - f"Accented char '{ch}' found in local trigger '{trigger}'" - ) + for trigger in match.get("triggers", []): + for ch in ACCENTED_CHARS: + assert ch not in trigger, ( + f"Accented char '{ch}' found in local trigger '{trigger}'" + )