We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
formatter accuracy
This has come up a couple of times. I suspect you'll improve formatter accuracy if you make these changes:

"allcaps" -> "all cap"
"alldown" -> "all down"

The reason for this is e.g. "alldown" is not an english word, D sounds like T, so "alld" sounds like alt, and you get "alt down".
Same for allcaps. Allc sounds a bit like alt, and you get "alt cap".

allcaps has a second problem. If you say "allcaps something", the S S (cap(s s)omething) chain will blend together in your speech and you'll get something that might sound more like "alt cap something". Hence removing the S.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `code/formatters.py`
Content:
```
1 from talon import Module, Context, actions, ui, imgui, app
2 from talon.grammar import Phrase
3 from typing import List, Union
4 import logging
5 import re
6 
7 ctx = Context()
8 key = actions.key
9 edit = actions.edit
10 
11 words_to_keep_lowercase = "a an the at by for in is of on to up and as but or nor".split()
12 
13 # The last phrase spoken, without & with formatting. Used for reformatting.
14 last_phrase = ""
15 last_phrase_formatted = ""
16 
17 # Internally, a formatter is a pair (sep, fn).
18 #
19 # - sep: a boolean, true iff the formatter should leave spaces between words.
20 #   We use SEP & NOSEP for this for clarity.
21 #
22 # - fn: a function (i, word, is_end) --> formatted_word, called on each `word`.
23 #   `i` is the word's index in the list, and `is_end` is True iff it's the
24 #   last word in the list.
25 SEP = True
26 NOSEP = False
27 
28 
29 def format_phrase(m: Union[str, Phrase], formatters: str):
30     global last_phrase, last_phrase_formatted
31     last_phrase = m
32     words = []
33     if isinstance(m, str):
34         words = m.split(" ")
35     else:
36         # FIXME: I believe this is no longer necessary. -rntz, 2022-02-10
37         if m.words[-1] == "over":
38             m.words = m.words[:-1]
39         words = actions.dictate.replace_words(actions.dictate.parse_words(m))
40 
41     result = last_phrase_formatted = format_phrase_without_adding_to_history(words, formatters)
42     actions.user.add_phrase_to_history(result)
43     # Arguably, we shouldn't be dealing with history here, but somewhere later
44     # down the line. But we have a bunch of code that relies on doing it this
45     # way and I don't feel like rewriting it just now. -rntz, 2020-11-04
46     return result
47 
48 
49 def format_phrase_without_adding_to_history(word_list, formatters: str):
50     # A formatter is a pair (keep_spaces, function). We drop spaces if any
51     # formatter does; we apply their functions in reverse order.
52     formatters = [all_formatters[name] for name in formatters.split(',')]
53     separator = ' ' if all(x[0] for x in formatters) else ''
54     functions = [x[1] for x in reversed(formatters)]
55     words = []
56     for i, word in enumerate(word_list):
57         for f in functions:
58             word = f(i, word, i == len(word_list) - 1)
59         words.append(word)
60     return separator.join(words)
61 
62 
63 # Formatter helpers
64 def surround(by):
65     return lambda i, word, last: (by if i == 0 else '') + word + (by if last else '')
66 
67 
68 def words_with_joiner(joiner):
69     """Pass through words unchanged, but add a separator between them."""
70     return (NOSEP, lambda i, word, _: ('' if i == 0 else joiner) + word)
71 
72 
73 def first_vs_rest(first_func, rest_func=lambda w: w):
74     """Supply one or two transformer functions for the first and rest of
75     words respectively.
76 
77     Leave second argument out if you want all but the first word to be passed
78     through unchanged.
79     Set first argument to None if you want the first word to be passed
80     through unchanged.
81     """
82     first_func = first_func or (lambda w: w)
83     return lambda i, word, _: first_func(word) if i == 0 else rest_func(word)
84 
85 
86 def every_word(word_func):
87     """Apply one function to every word."""
88     return lambda i, word, _: word_func(word)
89 
90 
91 formatters_dict = {
92     "NOOP": (SEP, lambda i, word, _: word),
93     "DOUBLE_UNDERSCORE": (NOSEP, first_vs_rest(lambda w: "__%s__" % w)),
94     "PRIVATE_CAMEL_CASE": (NOSEP, first_vs_rest(lambda w: w.lower(), lambda w: w.capitalize())),
95     "PROTECTED_CAMEL_CASE": (NOSEP, first_vs_rest(lambda w: w.lower(), lambda w: w.capitalize())),
96     "PUBLIC_CAMEL_CASE": (NOSEP, every_word(lambda w: w.capitalize())),
97     "SNAKE_CASE": (
98         NOSEP,
99         first_vs_rest(lambda w: w.lower(), lambda w: "_" + w.lower()),
100     ),
101     "NO_SPACES": (NOSEP, every_word(lambda w: w)),
102     "DASH_SEPARATED": words_with_joiner("-"),
103     "TERMINAL_DASH_SEPARATED": (
104         NOSEP,
105         first_vs_rest(lambda w: " --" + w.lower(), lambda w: "-" + w.lower()),
106     ),
107     "DOUBLE_COLON_SEPARATED": words_with_joiner("::"),
108     "ALL_CAPS": (SEP, every_word(lambda w: w.upper())),
109     "ALL_LOWERCASE": (SEP, every_word(lambda w: w.lower())),
110     "DOUBLE_QUOTED_STRING": (SEP, surround('"')),
111     "SINGLE_QUOTED_STRING": (SEP, surround("'")),
112     "SPACE_SURROUNDED_STRING": (SEP, surround(" ")),
113     "DOT_SEPARATED": words_with_joiner("."),
114     "DOT_SNAKE": (NOSEP, lambda i, word, _: "." + word if i == 0 else "_" + word),
115     "SLASH_SEPARATED": (NOSEP, every_word(lambda w: "/" + w)),
116     "CAPITALIZE_FIRST_WORD": (SEP, first_vs_rest(lambda w: w.capitalize())),
117     "CAPITALIZE_ALL_WORDS": (
118         SEP,
119         lambda i, word, _: word.capitalize()
120         if i == 0 or word not in words_to_keep_lowercase
121         else word,
122     ),
123 }
124 
125 # This is the mapping from spoken phrases to formatters
126 formatters_words = {
127     "all caps": formatters_dict["ALL_CAPS"],
128     "all down": formatters_dict["ALL_LOWERCASE"],
129     "camel": formatters_dict["PRIVATE_CAMEL_CASE"],
130     "dotted": formatters_dict["DOT_SEPARATED"],
131     "dub string": formatters_dict["DOUBLE_QUOTED_STRING"],
132     "dunder": formatters_dict["DOUBLE_UNDERSCORE"],
133     "hammer": formatters_dict["PUBLIC_CAMEL_CASE"],
134     "kebab": formatters_dict["DASH_SEPARATED"],
135     "packed": formatters_dict["DOUBLE_COLON_SEPARATED"],
136     "padded": formatters_dict["SPACE_SURROUNDED_STRING"],
137     "slasher": formatters_dict["SLASH_SEPARATED"],
138     "smash": formatters_dict["NO_SPACES"],
139     "snake": formatters_dict["SNAKE_CASE"],
140     "string": formatters_dict["SINGLE_QUOTED_STRING"],
141     "title": formatters_dict["CAPITALIZE_ALL_WORDS"],
142 }
143 
144 all_formatters = {}
145 all_formatters.update(formatters_dict)
146 all_formatters.update(formatters_words)
147 
148 mod = Module()
149 mod.list("formatters", desc="list of formatters")
150 mod.list(
151     "prose_formatter",
152     desc="words to start dictating prose, and the formatter they apply",
153 )
154 
155 
156 @mod.capture(rule="{self.formatters}+")
157 def formatters(m) -> str:
158     "Returns a comma-separated string of formatters e.g. 'SNAKE,DUBSTRING'"
159     return ",".join(m.formatters_list)
160 
161 
162 @mod.capture(
163     # Note that if the user speaks something like "snake dot", it will
164     # insert "dot" - otherwise, they wouldn't be able to insert punctuation
165     # words directly.
166     rule="<self.formatters> <user.text> (<user.text> | <user.formatter_immune>)*"
167 )
168 def format_text(m) -> str:
169     "Formats the text and returns a string"
170     out = ""
171     formatters = m[0]
172     for chunk in m[1:]:
173         if isinstance(chunk, ImmuneString):
174             out += chunk.string
175         else:
176             out += format_phrase(chunk, formatters)
177     return out
178 
179 
180 class ImmuneString(object):
181     """Wrapper that makes a string immune from formatting."""
182 
183     def __init__(self, string):
184         self.string = string
185 
186 
187 @mod.capture(
188     # Add anything else into this that you want to be able to speak during a
189     # formatter.
190     rule="(<user.symbol_key> | numb <number>)"
191 )
192 def formatter_immune(m) -> ImmuneString:
193     """Text that can be interspersed into a formatter, e.g. characters.
194 
195     It will be inserted directly, without being formatted.
196 
197     """
198     if hasattr(m, "number"):
199         value = m.number
200     else:
201         value = m[0]
202     return ImmuneString(str(value))
203 
204 
205 @mod.action_class
206 class Actions:
207     def formatted_text(phrase: Union[str, Phrase], formatters: str) -> str:
208         """Formats a phrase according to formatters. formatters is a comma-separated string of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')"""
209         return format_phrase(phrase, formatters)
210 
211     def insert_formatted(phrase: Union[str, Phrase], formatters: str):
212         """Inserts a phrase formatted according to formatters. Formatters is a comma separated list of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')"""
213         actions.insert(format_phrase(phrase, formatters))
214 
215     def insert_with_history(text: str):
216         """Inserts some text, remembering it in the phrase history."""
217         actions.user.add_phrase_to_history(text)
218         actions.insert(text)
219 
220     def formatters_reformat_last(formatters: str) -> str:
221         """Clears and reformats last formatted phrase"""
222         global last_phrase, last_phrase_formatted
223         if actions.user.get_last_phrase() != last_phrase_formatted:
224             # The last thing we inserted isn't the same as the last thing we
225             # formatted, so abort.
226             logging.warning(
227                 "formatters_reformat_last(): Last phrase wasn't a formatter!"
228             )
229             return
230         actions.user.clear_last_phrase()
231         actions.user.insert_formatted(last_phrase, formatters)
232 
233     def formatters_reformat_selection(formatters: str) -> str:
234         """Reformats the current selection."""
235         selected = edit.selected_text()
236         if not selected:
237             print("Asked to reformat selection, but nothing selected!")
238             return
239         unformatted = unformat_text(selected)
240         # Delete separately for compatibility with programs that don't overwrite
241         # selected text (e.g. Emacs)
242         edit.delete()
243         text = actions.self.formatted_text(unformatted, formatters)
244         actions.insert(text)
245         return text
246 
247     def get_formatters_words():
248         """returns a list of words currently used as formatters, and a demonstration string using those formatters"""
249         formatters_help_demo = {}
250         for name in sorted(set(formatters_words.keys())):
251             formatters_help_demo[name] = format_phrase_without_adding_to_history(['one', 'two', 'three'], name)
252         return  formatters_help_demo
253 
254     def reformat_text(text: str, formatters: str) -> str:
255         """Reformat the text."""
256         unformatted = unformat_text(text)
257         return actions.user.formatted_text(unformatted, formatters)
258 
259     def insert_many(strings: List[str]) -> None:
260         """Insert a list of strings, sequentially."""
261         for string in strings:
262             actions.insert(string)
263 
264 def unformat_text(text: str) -> str:
265     """Remove format from text"""
266     unformatted = re.sub(r"[^\w]+", " ", text)
267     # Split on camelCase, including numbers
268     # FIXME: handle non-ASCII letters!
269     unformatted = re.sub(r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])|(?<=[0-9])(?=[a-zA-Z])", " ", unformatted)
270     # TODO: Separate out studleycase vars
271     return unformatted.lower()
272 
273 
274 ctx.lists["self.formatters"] = formatters_words.keys()
275 ctx.lists["self.prose_formatter"] = {
276     "say": "NOOP",
277     "speak": "NOOP",
278     "sentence": "CAPITALIZE_FIRST_WORD",
279 }
280
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/code/formatters.py b/code/formatters.py
--- a/code/formatters.py
+++ b/code/formatters.py
@@ -124,7 +124,7 @@
 
 # This is the mapping from spoken phrases to formatters
 formatters_words = {
-    "all caps": formatters_dict["ALL_CAPS"],
+    "all cap": formatters_dict["ALL_CAPS"],
     "all down": formatters_dict["ALL_LOWERCASE"],
     "camel": formatters_dict["PRIVATE_CAMEL_CASE"],
     "dotted": formatters_dict["DOT_SEPARATED"],

{"golden_diff": "diff --git a/code/formatters.py b/code/formatters.py\n--- a/code/formatters.py\n+++ b/code/formatters.py\n@@ -124,7 +124,7 @@\n \n # This is the mapping from spoken phrases to formatters\n formatters_words = {\n-    \"all caps\": formatters_dict[\"ALL_CAPS\"],\n+    \"all cap\": formatters_dict[\"ALL_CAPS\"],\n     \"all down\": formatters_dict[\"ALL_LOWERCASE\"],\n     \"camel\": formatters_dict[\"PRIVATE_CAMEL_CASE\"],\n     \"dotted\": formatters_dict[\"DOT_SEPARATED\"],\n", "issue": "formatter accuracy\nThis has come up a couple of times. I suspect you'll improve formatter accuracy if you make these changes:\r\n\r\n\"allcaps\" -> \"all cap\"\r\n\"alldown\" -> \"all down\"\r\n\r\nThe reason for this is e.g. \"alldown\" is not an english word, D sounds like T, so \"alld\" sounds like alt, and you get \"alt down\".\r\nSame for allcaps. Allc sounds a bit like alt, and you get \"alt cap\".\r\n\r\nallcaps has a second problem. If you say \"allcaps something\", the S S (cap(s s)omething) chain will blend together in your speech and you'll get something that might sound more like \"alt cap something\". Hence removing the S.\n", "before_files": [{"content": "from talon import Module, Context, actions, ui, imgui, app\nfrom talon.grammar import Phrase\nfrom typing import List, Union\nimport logging\nimport re\n\nctx = Context()\nkey = actions.key\nedit = actions.edit\n\nwords_to_keep_lowercase = \"a an the at by for in is of on to up and as but or nor\".split()\n\n# The last phrase spoken, without & with formatting. Used for reformatting.\nlast_phrase = \"\"\nlast_phrase_formatted = \"\"\n\n# Internally, a formatter is a pair (sep, fn).\n#\n# - sep: a boolean, true iff the formatter should leave spaces between words.\n#   We use SEP & NOSEP for this for clarity.\n#\n# - fn: a function (i, word, is_end) --> formatted_word, called on each `word`.\n#   `i` is the word's index in the list, and `is_end` is True iff it's the\n#   last word in the list.\nSEP = True\nNOSEP = False\n\n\ndef format_phrase(m: Union[str, Phrase], formatters: str):\n    global last_phrase, last_phrase_formatted\n    last_phrase = m\n    words = []\n    if isinstance(m, str):\n        words = m.split(\" \")\n    else:\n        # FIXME: I believe this is no longer necessary. -rntz, 2022-02-10\n        if m.words[-1] == \"over\":\n            m.words = m.words[:-1]\n        words = actions.dictate.replace_words(actions.dictate.parse_words(m))\n\n    result = last_phrase_formatted = format_phrase_without_adding_to_history(words, formatters)\n    actions.user.add_phrase_to_history(result)\n    # Arguably, we shouldn't be dealing with history here, but somewhere later\n    # down the line. But we have a bunch of code that relies on doing it this\n    # way and I don't feel like rewriting it just now. -rntz, 2020-11-04\n    return result\n\n\ndef format_phrase_without_adding_to_history(word_list, formatters: str):\n    # A formatter is a pair (keep_spaces, function). We drop spaces if any\n    # formatter does; we apply their functions in reverse order.\n    formatters = [all_formatters[name] for name in formatters.split(',')]\n    separator = ' ' if all(x[0] for x in formatters) else ''\n    functions = [x[1] for x in reversed(formatters)]\n    words = []\n    for i, word in enumerate(word_list):\n        for f in functions:\n            word = f(i, word, i == len(word_list) - 1)\n        words.append(word)\n    return separator.join(words)\n\n\n# Formatter helpers\ndef surround(by):\n    return lambda i, word, last: (by if i == 0 else '') + word + (by if last else '')\n\n\ndef words_with_joiner(joiner):\n    \"\"\"Pass through words unchanged, but add a separator between them.\"\"\"\n    return (NOSEP, lambda i, word, _: ('' if i == 0 else joiner) + word)\n\n\ndef first_vs_rest(first_func, rest_func=lambda w: w):\n    \"\"\"Supply one or two transformer functions for the first and rest of\n    words respectively.\n\n    Leave second argument out if you want all but the first word to be passed\n    through unchanged.\n    Set first argument to None if you want the first word to be passed\n    through unchanged.\n    \"\"\"\n    first_func = first_func or (lambda w: w)\n    return lambda i, word, _: first_func(word) if i == 0 else rest_func(word)\n\n\ndef every_word(word_func):\n    \"\"\"Apply one function to every word.\"\"\"\n    return lambda i, word, _: word_func(word)\n\n\nformatters_dict = {\n    \"NOOP\": (SEP, lambda i, word, _: word),\n    \"DOUBLE_UNDERSCORE\": (NOSEP, first_vs_rest(lambda w: \"__%s__\" % w)),\n    \"PRIVATE_CAMEL_CASE\": (NOSEP, first_vs_rest(lambda w: w.lower(), lambda w: w.capitalize())),\n    \"PROTECTED_CAMEL_CASE\": (NOSEP, first_vs_rest(lambda w: w.lower(), lambda w: w.capitalize())),\n    \"PUBLIC_CAMEL_CASE\": (NOSEP, every_word(lambda w: w.capitalize())),\n    \"SNAKE_CASE\": (\n        NOSEP,\n        first_vs_rest(lambda w: w.lower(), lambda w: \"_\" + w.lower()),\n    ),\n    \"NO_SPACES\": (NOSEP, every_word(lambda w: w)),\n    \"DASH_SEPARATED\": words_with_joiner(\"-\"),\n    \"TERMINAL_DASH_SEPARATED\": (\n        NOSEP,\n        first_vs_rest(lambda w: \" --\" + w.lower(), lambda w: \"-\" + w.lower()),\n    ),\n    \"DOUBLE_COLON_SEPARATED\": words_with_joiner(\"::\"),\n    \"ALL_CAPS\": (SEP, every_word(lambda w: w.upper())),\n    \"ALL_LOWERCASE\": (SEP, every_word(lambda w: w.lower())),\n    \"DOUBLE_QUOTED_STRING\": (SEP, surround('\"')),\n    \"SINGLE_QUOTED_STRING\": (SEP, surround(\"'\")),\n    \"SPACE_SURROUNDED_STRING\": (SEP, surround(\" \")),\n    \"DOT_SEPARATED\": words_with_joiner(\".\"),\n    \"DOT_SNAKE\": (NOSEP, lambda i, word, _: \".\" + word if i == 0 else \"_\" + word),\n    \"SLASH_SEPARATED\": (NOSEP, every_word(lambda w: \"/\" + w)),\n    \"CAPITALIZE_FIRST_WORD\": (SEP, first_vs_rest(lambda w: w.capitalize())),\n    \"CAPITALIZE_ALL_WORDS\": (\n        SEP,\n        lambda i, word, _: word.capitalize()\n        if i == 0 or word not in words_to_keep_lowercase\n        else word,\n    ),\n}\n\n# This is the mapping from spoken phrases to formatters\nformatters_words = {\n    \"all caps\": formatters_dict[\"ALL_CAPS\"],\n    \"all down\": formatters_dict[\"ALL_LOWERCASE\"],\n    \"camel\": formatters_dict[\"PRIVATE_CAMEL_CASE\"],\n    \"dotted\": formatters_dict[\"DOT_SEPARATED\"],\n    \"dub string\": formatters_dict[\"DOUBLE_QUOTED_STRING\"],\n    \"dunder\": formatters_dict[\"DOUBLE_UNDERSCORE\"],\n    \"hammer\": formatters_dict[\"PUBLIC_CAMEL_CASE\"],\n    \"kebab\": formatters_dict[\"DASH_SEPARATED\"],\n    \"packed\": formatters_dict[\"DOUBLE_COLON_SEPARATED\"],\n    \"padded\": formatters_dict[\"SPACE_SURROUNDED_STRING\"],\n    \"slasher\": formatters_dict[\"SLASH_SEPARATED\"],\n    \"smash\": formatters_dict[\"NO_SPACES\"],\n    \"snake\": formatters_dict[\"SNAKE_CASE\"],\n    \"string\": formatters_dict[\"SINGLE_QUOTED_STRING\"],\n    \"title\": formatters_dict[\"CAPITALIZE_ALL_WORDS\"],\n}\n\nall_formatters = {}\nall_formatters.update(formatters_dict)\nall_formatters.update(formatters_words)\n\nmod = Module()\nmod.list(\"formatters\", desc=\"list of formatters\")\nmod.list(\n    \"prose_formatter\",\n    desc=\"words to start dictating prose, and the formatter they apply\",\n)\n\n\n@mod.capture(rule=\"{self.formatters}+\")\ndef formatters(m) -> str:\n    \"Returns a comma-separated string of formatters e.g. 'SNAKE,DUBSTRING'\"\n    return \",\".join(m.formatters_list)\n\n\n@mod.capture(\n    # Note that if the user speaks something like \"snake dot\", it will\n    # insert \"dot\" - otherwise, they wouldn't be able to insert punctuation\n    # words directly.\n    rule=\"<self.formatters> <user.text> (<user.text> | <user.formatter_immune>)*\"\n)\ndef format_text(m) -> str:\n    \"Formats the text and returns a string\"\n    out = \"\"\n    formatters = m[0]\n    for chunk in m[1:]:\n        if isinstance(chunk, ImmuneString):\n            out += chunk.string\n        else:\n            out += format_phrase(chunk, formatters)\n    return out\n\n\nclass ImmuneString(object):\n    \"\"\"Wrapper that makes a string immune from formatting.\"\"\"\n\n    def __init__(self, string):\n        self.string = string\n\n\n@mod.capture(\n    # Add anything else into this that you want to be able to speak during a\n    # formatter.\n    rule=\"(<user.symbol_key> | numb <number>)\"\n)\ndef formatter_immune(m) -> ImmuneString:\n    \"\"\"Text that can be interspersed into a formatter, e.g. characters.\n\n    It will be inserted directly, without being formatted.\n\n    \"\"\"\n    if hasattr(m, \"number\"):\n        value = m.number\n    else:\n        value = m[0]\n    return ImmuneString(str(value))\n\n\n@mod.action_class\nclass Actions:\n    def formatted_text(phrase: Union[str, Phrase], formatters: str) -> str:\n        \"\"\"Formats a phrase according to formatters. formatters is a comma-separated string of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')\"\"\"\n        return format_phrase(phrase, formatters)\n\n    def insert_formatted(phrase: Union[str, Phrase], formatters: str):\n        \"\"\"Inserts a phrase formatted according to formatters. Formatters is a comma separated list of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')\"\"\"\n        actions.insert(format_phrase(phrase, formatters))\n\n    def insert_with_history(text: str):\n        \"\"\"Inserts some text, remembering it in the phrase history.\"\"\"\n        actions.user.add_phrase_to_history(text)\n        actions.insert(text)\n\n    def formatters_reformat_last(formatters: str) -> str:\n        \"\"\"Clears and reformats last formatted phrase\"\"\"\n        global last_phrase, last_phrase_formatted\n        if actions.user.get_last_phrase() != last_phrase_formatted:\n            # The last thing we inserted isn't the same as the last thing we\n            # formatted, so abort.\n            logging.warning(\n                \"formatters_reformat_last(): Last phrase wasn't a formatter!\"\n            )\n            return\n        actions.user.clear_last_phrase()\n        actions.user.insert_formatted(last_phrase, formatters)\n\n    def formatters_reformat_selection(formatters: str) -> str:\n        \"\"\"Reformats the current selection.\"\"\"\n        selected = edit.selected_text()\n        if not selected:\n            print(\"Asked to reformat selection, but nothing selected!\")\n            return\n        unformatted = unformat_text(selected)\n        # Delete separately for compatibility with programs that don't overwrite\n        # selected text (e.g. Emacs)\n        edit.delete()\n        text = actions.self.formatted_text(unformatted, formatters)\n        actions.insert(text)\n        return text\n\n    def get_formatters_words():\n        \"\"\"returns a list of words currently used as formatters, and a demonstration string using those formatters\"\"\"\n        formatters_help_demo = {}\n        for name in sorted(set(formatters_words.keys())):\n            formatters_help_demo[name] = format_phrase_without_adding_to_history(['one', 'two', 'three'], name)\n        return  formatters_help_demo\n\n    def reformat_text(text: str, formatters: str) -> str:\n        \"\"\"Reformat the text.\"\"\"\n        unformatted = unformat_text(text)\n        return actions.user.formatted_text(unformatted, formatters)\n\n    def insert_many(strings: List[str]) -> None:\n        \"\"\"Insert a list of strings, sequentially.\"\"\"\n        for string in strings:\n            actions.insert(string)\n\ndef unformat_text(text: str) -> str:\n    \"\"\"Remove format from text\"\"\"\n    unformatted = re.sub(r\"[^\\w]+\", \" \", text)\n    # Split on camelCase, including numbers\n    # FIXME: handle non-ASCII letters!\n    unformatted = re.sub(r\"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])|(?<=[0-9])(?=[a-zA-Z])\", \" \", unformatted)\n    # TODO: Separate out studleycase vars\n    return unformatted.lower()\n\n\nctx.lists[\"self.formatters\"] = formatters_words.keys()\nctx.lists[\"self.prose_formatter\"] = {\n    \"say\": \"NOOP\",\n    \"speak\": \"NOOP\",\n    \"sentence\": \"CAPITALIZE_FIRST_WORD\",\n}\n", "path": "code/formatters.py"}], "after_files": [{"content": "from talon import Module, Context, actions, ui, imgui, app\nfrom talon.grammar import Phrase\nfrom typing import List, Union\nimport logging\nimport re\n\nctx = Context()\nkey = actions.key\nedit = actions.edit\n\nwords_to_keep_lowercase = \"a an the at by for in is of on to up and as but or nor\".split()\n\n# The last phrase spoken, without & with formatting. Used for reformatting.\nlast_phrase = \"\"\nlast_phrase_formatted = \"\"\n\n# Internally, a formatter is a pair (sep, fn).\n#\n# - sep: a boolean, true iff the formatter should leave spaces between words.\n#   We use SEP & NOSEP for this for clarity.\n#\n# - fn: a function (i, word, is_end) --> formatted_word, called on each `word`.\n#   `i` is the word's index in the list, and `is_end` is True iff it's the\n#   last word in the list.\nSEP = True\nNOSEP = False\n\n\ndef format_phrase(m: Union[str, Phrase], formatters: str):\n    global last_phrase, last_phrase_formatted\n    last_phrase = m\n    words = []\n    if isinstance(m, str):\n        words = m.split(\" \")\n    else:\n        # FIXME: I believe this is no longer necessary. -rntz, 2022-02-10\n        if m.words[-1] == \"over\":\n            m.words = m.words[:-1]\n        words = actions.dictate.replace_words(actions.dictate.parse_words(m))\n\n    result = last_phrase_formatted = format_phrase_without_adding_to_history(words, formatters)\n    actions.user.add_phrase_to_history(result)\n    # Arguably, we shouldn't be dealing with history here, but somewhere later\n    # down the line. But we have a bunch of code that relies on doing it this\n    # way and I don't feel like rewriting it just now. -rntz, 2020-11-04\n    return result\n\n\ndef format_phrase_without_adding_to_history(word_list, formatters: str):\n    # A formatter is a pair (keep_spaces, function). We drop spaces if any\n    # formatter does; we apply their functions in reverse order.\n    formatters = [all_formatters[name] for name in formatters.split(',')]\n    separator = ' ' if all(x[0] for x in formatters) else ''\n    functions = [x[1] for x in reversed(formatters)]\n    words = []\n    for i, word in enumerate(word_list):\n        for f in functions:\n            word = f(i, word, i == len(word_list) - 1)\n        words.append(word)\n    return separator.join(words)\n\n\n# Formatter helpers\ndef surround(by):\n    return lambda i, word, last: (by if i == 0 else '') + word + (by if last else '')\n\n\ndef words_with_joiner(joiner):\n    \"\"\"Pass through words unchanged, but add a separator between them.\"\"\"\n    return (NOSEP, lambda i, word, _: ('' if i == 0 else joiner) + word)\n\n\ndef first_vs_rest(first_func, rest_func=lambda w: w):\n    \"\"\"Supply one or two transformer functions for the first and rest of\n    words respectively.\n\n    Leave second argument out if you want all but the first word to be passed\n    through unchanged.\n    Set first argument to None if you want the first word to be passed\n    through unchanged.\n    \"\"\"\n    first_func = first_func or (lambda w: w)\n    return lambda i, word, _: first_func(word) if i == 0 else rest_func(word)\n\n\ndef every_word(word_func):\n    \"\"\"Apply one function to every word.\"\"\"\n    return lambda i, word, _: word_func(word)\n\n\nformatters_dict = {\n    \"NOOP\": (SEP, lambda i, word, _: word),\n    \"DOUBLE_UNDERSCORE\": (NOSEP, first_vs_rest(lambda w: \"__%s__\" % w)),\n    \"PRIVATE_CAMEL_CASE\": (NOSEP, first_vs_rest(lambda w: w.lower(), lambda w: w.capitalize())),\n    \"PROTECTED_CAMEL_CASE\": (NOSEP, first_vs_rest(lambda w: w.lower(), lambda w: w.capitalize())),\n    \"PUBLIC_CAMEL_CASE\": (NOSEP, every_word(lambda w: w.capitalize())),\n    \"SNAKE_CASE\": (\n        NOSEP,\n        first_vs_rest(lambda w: w.lower(), lambda w: \"_\" + w.lower()),\n    ),\n    \"NO_SPACES\": (NOSEP, every_word(lambda w: w)),\n    \"DASH_SEPARATED\": words_with_joiner(\"-\"),\n    \"TERMINAL_DASH_SEPARATED\": (\n        NOSEP,\n        first_vs_rest(lambda w: \" --\" + w.lower(), lambda w: \"-\" + w.lower()),\n    ),\n    \"DOUBLE_COLON_SEPARATED\": words_with_joiner(\"::\"),\n    \"ALL_CAPS\": (SEP, every_word(lambda w: w.upper())),\n    \"ALL_LOWERCASE\": (SEP, every_word(lambda w: w.lower())),\n    \"DOUBLE_QUOTED_STRING\": (SEP, surround('\"')),\n    \"SINGLE_QUOTED_STRING\": (SEP, surround(\"'\")),\n    \"SPACE_SURROUNDED_STRING\": (SEP, surround(\" \")),\n    \"DOT_SEPARATED\": words_with_joiner(\".\"),\n    \"DOT_SNAKE\": (NOSEP, lambda i, word, _: \".\" + word if i == 0 else \"_\" + word),\n    \"SLASH_SEPARATED\": (NOSEP, every_word(lambda w: \"/\" + w)),\n    \"CAPITALIZE_FIRST_WORD\": (SEP, first_vs_rest(lambda w: w.capitalize())),\n    \"CAPITALIZE_ALL_WORDS\": (\n        SEP,\n        lambda i, word, _: word.capitalize()\n        if i == 0 or word not in words_to_keep_lowercase\n        else word,\n    ),\n}\n\n# This is the mapping from spoken phrases to formatters\nformatters_words = {\n    \"all cap\": formatters_dict[\"ALL_CAPS\"],\n    \"all down\": formatters_dict[\"ALL_LOWERCASE\"],\n    \"camel\": formatters_dict[\"PRIVATE_CAMEL_CASE\"],\n    \"dotted\": formatters_dict[\"DOT_SEPARATED\"],\n    \"dub string\": formatters_dict[\"DOUBLE_QUOTED_STRING\"],\n    \"dunder\": formatters_dict[\"DOUBLE_UNDERSCORE\"],\n    \"hammer\": formatters_dict[\"PUBLIC_CAMEL_CASE\"],\n    \"kebab\": formatters_dict[\"DASH_SEPARATED\"],\n    \"packed\": formatters_dict[\"DOUBLE_COLON_SEPARATED\"],\n    \"padded\": formatters_dict[\"SPACE_SURROUNDED_STRING\"],\n    \"slasher\": formatters_dict[\"SLASH_SEPARATED\"],\n    \"smash\": formatters_dict[\"NO_SPACES\"],\n    \"snake\": formatters_dict[\"SNAKE_CASE\"],\n    \"string\": formatters_dict[\"SINGLE_QUOTED_STRING\"],\n    \"title\": formatters_dict[\"CAPITALIZE_ALL_WORDS\"],\n}\n\nall_formatters = {}\nall_formatters.update(formatters_dict)\nall_formatters.update(formatters_words)\n\nmod = Module()\nmod.list(\"formatters\", desc=\"list of formatters\")\nmod.list(\n    \"prose_formatter\",\n    desc=\"words to start dictating prose, and the formatter they apply\",\n)\n\n\n@mod.capture(rule=\"{self.formatters}+\")\ndef formatters(m) -> str:\n    \"Returns a comma-separated string of formatters e.g. 'SNAKE,DUBSTRING'\"\n    return \",\".join(m.formatters_list)\n\n\n@mod.capture(\n    # Note that if the user speaks something like \"snake dot\", it will\n    # insert \"dot\" - otherwise, they wouldn't be able to insert punctuation\n    # words directly.\n    rule=\"<self.formatters> <user.text> (<user.text> | <user.formatter_immune>)*\"\n)\ndef format_text(m) -> str:\n    \"Formats the text and returns a string\"\n    out = \"\"\n    formatters = m[0]\n    for chunk in m[1:]:\n        if isinstance(chunk, ImmuneString):\n            out += chunk.string\n        else:\n            out += format_phrase(chunk, formatters)\n    return out\n\n\nclass ImmuneString(object):\n    \"\"\"Wrapper that makes a string immune from formatting.\"\"\"\n\n    def __init__(self, string):\n        self.string = string\n\n\n@mod.capture(\n    # Add anything else into this that you want to be able to speak during a\n    # formatter.\n    rule=\"(<user.symbol_key> | numb <number>)\"\n)\ndef formatter_immune(m) -> ImmuneString:\n    \"\"\"Text that can be interspersed into a formatter, e.g. characters.\n\n    It will be inserted directly, without being formatted.\n\n    \"\"\"\n    if hasattr(m, \"number\"):\n        value = m.number\n    else:\n        value = m[0]\n    return ImmuneString(str(value))\n\n\n@mod.action_class\nclass Actions:\n    def formatted_text(phrase: Union[str, Phrase], formatters: str) -> str:\n        \"\"\"Formats a phrase according to formatters. formatters is a comma-separated string of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')\"\"\"\n        return format_phrase(phrase, formatters)\n\n    def insert_formatted(phrase: Union[str, Phrase], formatters: str):\n        \"\"\"Inserts a phrase formatted according to formatters. Formatters is a comma separated list of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')\"\"\"\n        actions.insert(format_phrase(phrase, formatters))\n\n    def insert_with_history(text: str):\n        \"\"\"Inserts some text, remembering it in the phrase history.\"\"\"\n        actions.user.add_phrase_to_history(text)\n        actions.insert(text)\n\n    def formatters_reformat_last(formatters: str) -> str:\n        \"\"\"Clears and reformats last formatted phrase\"\"\"\n        global last_phrase, last_phrase_formatted\n        if actions.user.get_last_phrase() != last_phrase_formatted:\n            # The last thing we inserted isn't the same as the last thing we\n            # formatted, so abort.\n            logging.warning(\n                \"formatters_reformat_last(): Last phrase wasn't a formatter!\"\n            )\n            return\n        actions.user.clear_last_phrase()\n        actions.user.insert_formatted(last_phrase, formatters)\n\n    def formatters_reformat_selection(formatters: str) -> str:\n        \"\"\"Reformats the current selection.\"\"\"\n        selected = edit.selected_text()\n        if not selected:\n            print(\"Asked to reformat selection, but nothing selected!\")\n            return\n        unformatted = unformat_text(selected)\n        # Delete separately for compatibility with programs that don't overwrite\n        # selected text (e.g. Emacs)\n        edit.delete()\n        text = actions.self.formatted_text(unformatted, formatters)\n        actions.insert(text)\n        return text\n\n    def get_formatters_words():\n        \"\"\"returns a list of words currently used as formatters, and a demonstration string using those formatters\"\"\"\n        formatters_help_demo = {}\n        for name in sorted(set(formatters_words.keys())):\n            formatters_help_demo[name] = format_phrase_without_adding_to_history(['one', 'two', 'three'], name)\n        return  formatters_help_demo\n\n    def reformat_text(text: str, formatters: str) -> str:\n        \"\"\"Reformat the text.\"\"\"\n        unformatted = unformat_text(text)\n        return actions.user.formatted_text(unformatted, formatters)\n\n    def insert_many(strings: List[str]) -> None:\n        \"\"\"Insert a list of strings, sequentially.\"\"\"\n        for string in strings:\n            actions.insert(string)\n\ndef unformat_text(text: str) -> str:\n    \"\"\"Remove format from text\"\"\"\n    unformatted = re.sub(r\"[^\\w]+\", \" \", text)\n    # Split on camelCase, including numbers\n    # FIXME: handle non-ASCII letters!\n    unformatted = re.sub(r\"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])|(?<=[0-9])(?=[a-zA-Z])\", \" \", unformatted)\n    # TODO: Separate out studleycase vars\n    return unformatted.lower()\n\n\nctx.lists[\"self.formatters\"] = formatters_words.keys()\nctx.lists[\"self.prose_formatter\"] = {\n    \"say\": \"NOOP\",\n    \"speak\": \"NOOP\",\n    \"sentence\": \"CAPITALIZE_FIRST_WORD\",\n}\n", "path": "code/formatters.py"}]}