dotfiles/talon/user/community/core/homophones/homophones.py

243 lines
7.2 KiB
Python

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()