import os from talon import Context, Module, actions, app, clip, fs, imgui, ui ######################################################################## # global settings ######################################################################## # a list of homophones where each line is a comma separated list # e.g. where,wear,ware # a suitable one can be found here: # https://github.com/pimentel/homophones cwd = os.path.dirname(os.path.realpath(__file__)) homophones_file = os.path.join(cwd, "homophones.csv") # if quick_replace, then when a word is selected and only one homophone exists, # replace it without bringing up the options quick_replace = True show_help = False ######################################################################## ctx = Context() mod = Module() mod.list("homophones_canonicals", desc="list of words ") mod.tag( "homophones_open", desc="Tag for enabling homophones commands when the associated gui is open", ) main_screen = ui.main_screen() def update_homophones(name, flags): if name != homophones_file: return phones = {} canonical_list = [] with open(homophones_file) as f: for line in f: words = line.rstrip().split(",") words = [x for x in words if x.strip() != ""] canonical_list.append(words[0]) merged_words = set(words) for word in words: old_words = phones.get(word.lower(), []) merged_words.update(old_words) merged_words = sorted(merged_words) for word in merged_words: phones[word.lower()] = merged_words global all_homophones all_homophones = phones ctx.lists["self.homophones_canonicals"] = canonical_list update_homophones(homophones_file, None) fs.watch(cwd, update_homophones) active_word_list = None is_selection = False def close_homophones(): gui.hide() ctx.tags = [] PHONES_FORMATTERS = [ lambda word: word.capitalize(), lambda word: word.upper(), ] def find_matching_format_function(word_with_formatting, format_functions): """Finds the formatter function from a list of formatter functions which transforms a word into itself. Returns an identity function if none exists""" for formatter in format_functions: formatted_word = formatter(word_with_formatting) if word_with_formatting == formatted_word: return formatter return lambda word: word def raise_homophones(word_to_find_homophones_for, forced=False, selection=False): global quick_replace global active_word_list global show_help global force_raise global is_selection force_raise = forced is_selection = selection if is_selection: word_to_find_homophones_for = word_to_find_homophones_for.strip() formatter = find_matching_format_function( word_to_find_homophones_for, PHONES_FORMATTERS ) word_to_find_homophones_for = word_to_find_homophones_for.lower() # We support plurals, but very naively. If we can't find your word but your word ends in an s, presume its plural # and attempt to find the singular, then present the presumed plurals back. This could be improved! if word_to_find_homophones_for in all_homophones: valid_homophones = all_homophones[word_to_find_homophones_for] elif ( word_to_find_homophones_for.endswith("s") and word_to_find_homophones_for[:-1] in all_homophones ): valid_homophones = map( lambda w: w + "s", all_homophones[word_to_find_homophones_for[:-1]] ) else: app.notify( "homophones.py", f'"{word_to_find_homophones_for}" not in homophones list' ) return # Move current word to end of list to reduce searcher's cognitive load valid_homophones_reordered = list( filter( lambda word_from_list: word_from_list.lower() != word_to_find_homophones_for, valid_homophones, ) ) + [word_to_find_homophones_for] active_word_list = list(map(formatter, valid_homophones_reordered)) if ( is_selection and len(active_word_list) == 2 and quick_replace and not force_raise ): if word_to_find_homophones_for == active_word_list[0].lower(): new = active_word_list[1] else: new = active_word_list[0] clip.set(new) actions.edit.paste() return ctx.tags = ["user.homophones_open"] show_help = False gui.show() @imgui.open(x=main_screen.x + main_screen.width / 2.6, y=main_screen.y) def gui(gui: imgui.GUI): global active_word_list if show_help: gui.text("Homophone help - todo") else: gui.text("Select a homophone") gui.line() index = 1 for word in active_word_list: if gui.button(f"Choose {index}: {word}"): actions.insert(actions.user.homophones_select(index)) actions.user.homophones_hide() index = index + 1 if gui.button("Phones hide"): actions.user.homophones_hide() def show_help_gui(): global show_help show_help = True gui.show() @mod.capture(rule="{self.homophones_canonicals}") def homophones_canonical(m) -> str: "Returns a single string" return m.homophones_canonicals @mod.action_class class Actions: def homophones_hide(): """Hides the homophones display""" close_homophones() def homophones_show(m: str): """Show the homophones display""" raise_homophones(m, False, False) def homophones_show_auto(): """Show homophones for selection, or current word if selection is empty.""" text = actions.edit.selected_text() if text: raise_homophones(text, False, True) else: actions.edit.select_word() actions.user.homophones_show_selection() def homophones_show_selection(): """Show the homophones display for the selected text""" raise_homophones(actions.edit.selected_text(), False, True) def homophones_force_show(m: str): """Show the homophones display forcibly""" raise_homophones(m, True, False) def homophones_force_show_selection(): """Show the homophones display for the selected text forcibly""" raise_homophones(actions.edit.selected_text(), True, True) def homophones_select(number: int) -> str: """selects the homophone by number""" if number <= len(active_word_list) and number > 0: return active_word_list[number - 1] error = "homophones.py index {} is out of range (1-{})".format( number, len(active_word_list) ) app.notify(error) raise error def homophones_get(word: str) -> [str] or None: """Get homophones for the given word""" word = word.lower() if word in all_homophones: return all_homophones[word] return None ctx_homophones_open = Context() ctx_homophones_open.matches = """ tag: user.homophones_open """ @ctx_homophones_open.action_class("user") class UserActions: def choose(number_small: int): """Choose the nth homophone""" result = actions.user.homophones_select(number_small) actions.insert(result) actions.user.homophones_hide()