Skip to content

Instantly share code, notes, and snippets.

@gs-niteesh
Created June 9, 2021 19:46
Show Gist options
  • Save gs-niteesh/8d025a1f83653282de4e5c137bdd7d51 to your computer and use it in GitHub Desktop.
Save gs-niteesh/8d025a1f83653282de4e5c137bdd7d51 to your computer and use it in GitHub Desktop.
aqmp_autocomplete_prototype.py
import urwid
import complete
from blinker import Signal
import logging
logging.basicConfig(filename='log.txt', level=logging.INFO)
start_suggestion = Signal()
select_suggestion = Signal()
reset_suggestion = Signal()
load_help_text = Signal()
typeable_chars = "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY"
class QEdit(urwid.Edit):
def __init__(self):
super().__init__()
def keypress(self, size, key):
text = self.get_edit_text()
logging.info('QEdit keypress: %s', key)
if key in typeable_chars or key == 'backspace':
# Send keys to Suggestion Widget.
if key == 'backspace' and text:
start_suggestion.send(self, text=text[:-1])
else:
start_suggestion.send(self, text=text + key)
elif key == 'tab':
# On pressing tab key, emit signal to inform the suggestion widget
# to highlight the suggestion word and move to the next suggestion
# word on pressing repeadtly.
select_suggestion.send(self, text=text)
return
elif key == 'enter':
# Inform the suggestion widget to select the current highlighted
# word and display its help text.
load_help_text.send(self)
else:
reset_suggestion.send(self)
return super().keypress(size, key)
# Stolen from mitmproxy (slightly modified)
class SuggestionsWidget(urwid.Columns):
def __init__(self):
super().__init__([], dividechars=1)
self._last_suggestions = None
def populate(self, suggestions):
if not suggestions:
self.clear()
return
for item in suggestions:
self._append(item, 'suggestion')
self._last_suggestions = suggestions
def highlight_suggestion(self, index):
counter = 0
for col in self.contents:
text, attr = col[0].original_widget.get_text()
if counter != index:
attr = 'suggestion'
else:
attr = 'suggestion:highlight'
col[0].original_widget.set_text((attr, text))
counter += 1
def _append(self, item, style='suggestion'):
self.contents.append((urwid.AttrMap(urwid.Text(item), style),
self.options('given', len(item), False)))
def clear(self):
self.contents.clear()
class HelpText(urwid.Frame):
def __init__(self, master):
super().__init__(
urwid.LineBox(urwid.Filler(urwid.Text('Show some help text')),
title='Help Text'))
self.master = master
def keypress(self, size, key):
if key == 'esc':
self.remove_overlay()
res = super().keypress(size, key)
return res
def remove_overlay(self):
# This idea of storing reference to master is taken from mitmproxy
# Many main widgets in mitmproxy store a reference to the consolemaster
# class.
self.master.remove_overlay()
class Editor(urwid.Frame):
def __init__(self):
self.edit = QEdit()
self.body = urwid.Filler(self.edit, valign='top')
self.footer = SuggestionsWidget()
start_suggestion.connect(self.start_suggestion)
select_suggestion.connect(self.select_suggestion)
reset_suggestion.connect(self.reset_suggestion)
load_help_text.connect(self.load_help_text)
self.showing_suggestions = False
self.overlay = HelpText(self)
self.idx = 0
super().__init__(self.body, footer=self.footer)
def remove_overlay(self):
# Should use a proper stack based window management system
self.body = self.last_window
def load_help_text(self, sender):
# Display an simple overlay
text = self.footer.contents[self.idx - 1][0].original_widget
self.edit.set_edit_text(text.text)
self.edit.edit_pos = len(text.text)
self.show_help_text()
self.footer.clear()
def show_help_text(self):
self.last_window = self.body
self.body = urwid.Overlay(self.overlay, self.body, 'center',
('relative', 80), 'top', ('relative', 80))
def reset_suggestion(self, sender):
# reset the suggestion widget state
if not self.showing_suggestions:
return
self.footer.clear()
self.idx = 0
self.showing_suggestions = False
def select_suggestion(self, sender, text):
# text - Here we again pass text when no suggestion is shown but the
# user presses tab.
# Highlightes one of the suggestion words
if not text:
return
if not self.showing_suggestions:
self.start_suggestion(self, text)
self.footer.highlight_suggestion(self.idx)
max_len = len(self.footer.contents)
self.idx = 0 if self.idx == max_len - 1 else self.idx + 1
def start_suggestion(self, sender, text=None):
# text - Input by user
# A simple suggestion mechanism that check if words in the dictionary
# start with text
logging.info('start_suggestion: %s', text)
if self.showing_suggestions:
self.idx = 0
self.footer.clear()
suggestions = []
if not text:
self.footer.clear()
self.showing_suggestions = False
return
for word in complete.suggestions:
word = word.lower()
if word.startswith(text):
suggestions.append(word)
self.footer.populate(suggestions)
self.showing_suggestions = True
class App:
def __init__(self):
self.widget = Editor()
palette = [
('highlight', 'black', 'light gray'),
('suggestion', '', ''),
('suggestion:highlight', 'light red', ''),
]
self.loop = urwid.MainLoop(self.widget, palette=palette)
def run(self):
self.loop.run()
if __name__ == '__main__':
logging.info('Starting App')
App().run()
suggestions = [
"Acumen",
"Adhere",
"Abolish",
"Abash",
"Abound",
"Abate",
"Abject",
"Abjure",
"Abortive",
"Absolve",
"Accord",
"Acrimony",
"Adamant",
"Adherent",
"Adjunct",
"Admonish",
"Adversity",
"Alien",
"Allay",
"Alleviate",
"Allure",
"Ascend",
"Amplify",
"Arraign",
"Audacity",
"Authentic",
"Awkward",
"Axiom",
"Baffle",
"Bewitching",
"Bleak",
"Brittle",
"Bustle",
"Barbarous",
"Baroque",
"Barren",
"Barrier",
"Base",
"Batty",
"Bawdy",
"Befogged",
"Benevolent",
"Benign",
"Bind",
"Bleak",
"Blunt",
"Boisterous",
"Bold",
"Busy",
"Calculating",
"Calamity",
"Callous",
"Calumny",
"Capable",
"Captivate",
"Captivity",
"Cease",
"Chaste",
"Chastise",
"Compassion",
"Comprise",
"Concede",
"Concur",
"Consent",
"Consequence",
"Consolidate",
"Conspicuous",
"Contempt",
"Contradict",
"Contrary",
"Calm",
"Callous",
"Camouflage",
"Candid",
"Captivate",
"Carnal",
"Catholic",
"Celebrated",
"Cement",
"Censure",
"Cheap",
"Clandestine",
"Classic",
"Coarse",
"Comic",
"Compact",
"Compress",
"Conceit",
"Concord",
"Condemn",
"Confident",
"Consolidate",
"Courtesy",
"Creation",
"Cunning",
"Decay",
"Deceit",
"Decipher",
"Defile",
"Defray",
"Deliberate",
"Demolish",
"Deprive",
"Deride",
"Disdain",
"Dissuade",
"Denounce",
"Dense",
"Derogatory",
"Despair",
"Destructive",
"Docile",
"Dwarf",
"Eager",
"Eclipse",
"Eccentric",
"Ecstasy",
"Efface",
"Eloquence",
"Encumbrance",
"Endeavour",
"Enormous",
"Epitome",
"Equivocal",
"Eradicate",
"Fabricate",
"Fallacy",
"Falter",
"Fanatical",
"Feeble",
"Ferocious",
"Feud",
"Fluctuate",
"Forsake",
"Fragile",
"Frantic",
"Frivolous",
"Frugality",
"Gather",
"Gloom",
"Glut",
"Gorgeous",
"Gracious",
"Grisly",
"Grudge",
"Guile",
"Generosity",
"Genuine",
"Gloomy",
"Glory",
"Hamper",
"Hapless",
"Harass",
"Haughty",
"Hazard",
"Heretic",
"Hideous",
"Hamstrung",
"Harmony",
"Hasty",
"Honor",
"Humble",
"Humility",
"Hypocrisy",
"Impenitent",
"Impulsive",
"Indifferent",
"Indigent",
"Infernal",
"Insipid",
"Interesting",
"Immaculate",
"Immense",
"Immerse",
"Imminent",
"Immunity",
"Impair",
"Impartial",
"Impediment",
"Impious",
"Impute",
"Inclination",
"Incompetent",
"Incongruous",
"Inevitable",
"Infringe",
"Ingenuous",
"Insinuate",
"Insipid",
"Insolvent",
"Instill",
"Intricate",
"Intrigue",
"Intrinsic",
"Invective",
"Invincible",
"Irrepressible",
"Immaculate",
"Jaded",
"Jejune",
"Jovial",
"Jubilant",
"Judicious",
"Just",
"Justify",
"Juvenile",
"Keen",
"Kindred",
"Knave",
"Knell",
"Knotty",
"Lavish",
"Lax",
"Lenient",
"Liable",
"Liberal",
"Linger",
"Lucid",
"Lunacy",
"Lure",
"Luscious",
"Luxuriant",
"Languid",
"Malice",
"Mandatory",
"Masculine",
"Merit",
"Miraculous",
"Mitigate",
"Modest",
"Molest",
"Mollify",
"Momentous",
"Monotonous",
"Morbid",
"Munificent",
"Murky",
"Mutinous",
"Mutual",
"Niggardly",
"Nimble",
"Nonchalant",
"Novice",
"Noxious",
"Nullify",
"Notion",
"Numerous",
"Obstruct",
"Obliging",
"Obscure",
"Obstinate",
"Obtain",
"Obvious",
"Odious",
"Offensive",
"Occult",
"Offspring",
"Ominous",
"Opaque",
"Optimist",
"Oracular",
"Ordain",
"Ornamental",
"Outbreak",
"Outrage",
"Pacify",
"Persuade",
"Perturbed",
"Propagate",
"Progress",
"Prompt",
"Prudence",
"Pamper",
"Paramount",
"Peerless",
"Peevish",
"Pertness",
"Perverse",
"Placid",
"Pompous",
"Precarious",
"Predicament",
"Quack",
"Quaint",
"Quarantine",
"Quell",
"Quibble",
"Raid",
"Rapidity",
"Reason",
"Rebellious",
"Rectify",
"Reluctant",
"Restrain",
"Ratify",
"Ravage",
"Redeem",
"Remnant",
"Remonstrate",
"Remorse",
"Rescind",
"Resentment",
"Retract",
"Reverence",
"Rout",
"Rustic",
"Ruthless",
"Sacred",
"Savage",
"Startled",
"Steep",
"Stranger",
"Sublime",
"Succinct",
"Sympathy",
"System",
"Sarcastic",
"Saucy",
"Shrewd",
"Scanty",
"Servile",
"Shabby",
"Slander",
"Sneer",
"Solicit",
"Subterfuge",
"Stain",
"Spurious",
"Sporadic",
"Spry",
"Squalid",
"Successful",
"Sterile",
"Stupor",
"Subsequent",
"Substantial",
"Subvert",
"Superficial",
"Sycophant",
"Taboo",
"Taciturn",
"Tedious",
"Temperate",
"Tenement",
"Tenacious",
"Throng",
"Timid",
"Tranquil",
"Transient",
"Trenchant",
"Treacherous",
"Trivial",
"Tumultuous",
"Tyro",
"Tame",
"Terse",
"Thick",
"Thrifty",
"Tranquil",
"Transparent",
"Tremble",
"Uncouth",
"Utterly",
"Umbrage",
"Uncouth",
"Urchin",
"Urge",
"Vain",
"Vagrant",
"Valor",
"Vanity",
"Venerable",
"Venom",
"Veteran",
"Vicious",
"Vigilant",
"Vivacious",
"Vilify",
"Vouch",
"Virtue",
"Vivid",
"Waive",
"Wan",
"Wane",
"Wary",
"Wed",
"Wicked",
"Wield",
"Wile",
"Winsome",
"Wilt",
"Yell",
"Yield",
"Yearn",
"Yoke",
"Zeal",
"Zenith",
"Zest",
"Zig",
"zag"
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment