11import csv
22from collections .abc import Container
3+ from dataclasses import dataclass
34from datetime import datetime
45from pathlib import Path
5- from typing import Callable , Optional
6+ from typing import Callable , Optional , TypedDict
67
78from talon import Context , Module , actions , app , fs
89
3536"""
3637
3738
39+ # Maps from Talon list name to a map from spoken form to value
3840ListToSpokenForms = dict [str , dict [str , str ]]
3941
4042
43+ @dataclass
44+ class SpokenFormEntry :
45+ list_name : str
46+ id : str
47+ spoken_forms : list [str ]
48+
49+
4150def init_csv_and_watch_changes (
4251 filename : str ,
4352 default_values : ListToSpokenForms ,
44- handle_new_values : Optional [Callable [[ListToSpokenForms ], None ]] = None ,
53+ handle_new_values : Optional [Callable [[list [ SpokenFormEntry ] ], None ]] = None ,
4554 extra_ignored_values : Optional [list [str ]] = None ,
4655 allow_unknown_values : bool = False ,
4756 default_list_name : Optional [str ] = None ,
@@ -69,7 +78,7 @@ def init_csv_and_watch_changes(
6978 `cursorles-settings` dir
7079 default_values (ListToSpokenForms): The default values for the lists to
7180 be customized in the given csv
72- handle_new_values (Optional[Callable[[ListToSpokenForms ], None]]): A
81+ handle_new_values (Optional[Callable[[list[SpokenFormEntry] ], None]]): A
7382 callback to be called when the lists are updated
7483 extra_ignored_values (Optional[list[str]]): Don't throw an exception if
7584 any of these appear as values; just ignore them and don't add them
@@ -185,18 +194,18 @@ def create_default_vocabulary_dicts(
185194
186195
187196def update_dicts (
188- default_values : dict [ str , dict ] ,
189- current_values : dict ,
197+ default_values : ListToSpokenForms ,
198+ current_values : dict [ str , str ] ,
190199 extra_ignored_values : list [str ],
191200 allow_unknown_values : bool ,
192201 default_list_name : Optional [str ],
193202 pluralize_lists : list [str ],
194- handle_new_values : Optional [Callable [[ListToSpokenForms ], None ]],
203+ handle_new_values : Optional [Callable [[list [ SpokenFormEntry ] ], None ]],
195204):
196205 # Create map with all default values
197- results_map = {}
198- for list_name , dict in default_values .items ():
199- for key , value in dict .items ():
206+ results_map : dict [ str , ResultsListEntry ] = {}
207+ for list_name , obj in default_values .items ():
208+ for key , value in obj .items ():
200209 results_map [value ] = {"key" : key , "value" : value , "list" : list_name }
201210
202211 # Update result with current values
@@ -206,7 +215,7 @@ def update_dicts(
206215 except KeyError :
207216 if value in extra_ignored_values :
208217 pass
209- elif allow_unknown_values :
218+ elif allow_unknown_values and default_list_name is not None :
210219 results_map [value ] = {
211220 "key" : key ,
212221 "value" : value ,
@@ -217,9 +226,35 @@ def update_dicts(
217226
218227 # Convert result map back to result list
219228 results = {res ["list" ]: {} for res in results_map .values ()}
220- for obj in results_map .values ():
229+ values : list [SpokenFormEntry ] = []
230+ for list_name , id , spoken_forms in generate_spoken_forms (
231+ list (results_map .values ())
232+ ):
233+ for spoken_form in spoken_forms :
234+ results [list_name ][spoken_form ] = id
235+ values .append (
236+ SpokenFormEntry (list_name = list_name , id = id , spoken_forms = spoken_forms )
237+ )
238+
239+ # Assign result to talon context list
240+ assign_lists_to_context (ctx , results , pluralize_lists )
241+
242+ if handle_new_values is not None :
243+ handle_new_values (values )
244+
245+
246+ class ResultsListEntry (TypedDict ):
247+ key : str
248+ value : str
249+ list : str
250+
251+
252+ def generate_spoken_forms (results_list : list [ResultsListEntry ]):
253+ for obj in results_list :
221254 value = obj ["value" ]
222255 key = obj ["key" ]
256+
257+ spoken = []
223258 if not is_removed (key ):
224259 for k in key .split ("|" ):
225260 if value == "pasteFromClipboard" and k .endswith (" to" ):
@@ -230,13 +265,13 @@ def update_dicts(
230265 # cursorless before this change would have "paste to" as
231266 # their spoken form and so would need to say "paste to to".
232267 k = k [:- 3 ]
233- results [ obj [ "list" ]][ k .strip ()] = value
268+ spoken . append ( k .strip ())
234269
235- # Assign result to talon context list
236- assign_lists_to_context ( ctx , results , pluralize_lists )
237-
238- if handle_new_values is not None :
239- handle_new_values ( results )
270+ yield (
271+ obj [ "list" ],
272+ value ,
273+ spoken ,
274+ )
240275
241276
242277def assign_lists_to_context (
0 commit comments