17db96d56Sopenharmony_ci'''Complete the current word before the cursor with words in the editor. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciEach menu selection or shortcut key selection replaces the word with a 47db96d56Sopenharmony_cidifferent word with the same prefix. The search for matches begins 57db96d56Sopenharmony_cibefore the target and moves toward the top of the editor. It then starts 67db96d56Sopenharmony_ciafter the cursor and moves down. It then returns to the original word and 77db96d56Sopenharmony_cithe cycle starts again. 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_ciChanging the current text line or leaving the cursor in a different 107db96d56Sopenharmony_ciplace before requesting the next selection causes AutoExpand to reset 117db96d56Sopenharmony_ciits state. 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_ciThere is only one instance of Autoexpand. 147db96d56Sopenharmony_ci''' 157db96d56Sopenharmony_ciimport re 167db96d56Sopenharmony_ciimport string 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_ciclass AutoExpand: 207db96d56Sopenharmony_ci wordchars = string.ascii_letters + string.digits + "_" 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ci def __init__(self, editwin): 237db96d56Sopenharmony_ci self.text = editwin.text 247db96d56Sopenharmony_ci self.bell = self.text.bell 257db96d56Sopenharmony_ci self.state = None 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci def expand_word_event(self, event): 287db96d56Sopenharmony_ci "Replace the current word with the next expansion." 297db96d56Sopenharmony_ci curinsert = self.text.index("insert") 307db96d56Sopenharmony_ci curline = self.text.get("insert linestart", "insert lineend") 317db96d56Sopenharmony_ci if not self.state: 327db96d56Sopenharmony_ci words = self.getwords() 337db96d56Sopenharmony_ci index = 0 347db96d56Sopenharmony_ci else: 357db96d56Sopenharmony_ci words, index, insert, line = self.state 367db96d56Sopenharmony_ci if insert != curinsert or line != curline: 377db96d56Sopenharmony_ci words = self.getwords() 387db96d56Sopenharmony_ci index = 0 397db96d56Sopenharmony_ci if not words: 407db96d56Sopenharmony_ci self.bell() 417db96d56Sopenharmony_ci return "break" 427db96d56Sopenharmony_ci word = self.getprevword() 437db96d56Sopenharmony_ci self.text.delete("insert - %d chars" % len(word), "insert") 447db96d56Sopenharmony_ci newword = words[index] 457db96d56Sopenharmony_ci index = (index + 1) % len(words) 467db96d56Sopenharmony_ci if index == 0: 477db96d56Sopenharmony_ci self.bell() # Warn we cycled around 487db96d56Sopenharmony_ci self.text.insert("insert", newword) 497db96d56Sopenharmony_ci curinsert = self.text.index("insert") 507db96d56Sopenharmony_ci curline = self.text.get("insert linestart", "insert lineend") 517db96d56Sopenharmony_ci self.state = words, index, curinsert, curline 527db96d56Sopenharmony_ci return "break" 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci def getwords(self): 557db96d56Sopenharmony_ci "Return a list of words that match the prefix before the cursor." 567db96d56Sopenharmony_ci word = self.getprevword() 577db96d56Sopenharmony_ci if not word: 587db96d56Sopenharmony_ci return [] 597db96d56Sopenharmony_ci before = self.text.get("1.0", "insert wordstart") 607db96d56Sopenharmony_ci wbefore = re.findall(r"\b" + word + r"\w+\b", before) 617db96d56Sopenharmony_ci del before 627db96d56Sopenharmony_ci after = self.text.get("insert wordend", "end") 637db96d56Sopenharmony_ci wafter = re.findall(r"\b" + word + r"\w+\b", after) 647db96d56Sopenharmony_ci del after 657db96d56Sopenharmony_ci if not wbefore and not wafter: 667db96d56Sopenharmony_ci return [] 677db96d56Sopenharmony_ci words = [] 687db96d56Sopenharmony_ci dict = {} 697db96d56Sopenharmony_ci # search backwards through words before 707db96d56Sopenharmony_ci wbefore.reverse() 717db96d56Sopenharmony_ci for w in wbefore: 727db96d56Sopenharmony_ci if dict.get(w): 737db96d56Sopenharmony_ci continue 747db96d56Sopenharmony_ci words.append(w) 757db96d56Sopenharmony_ci dict[w] = w 767db96d56Sopenharmony_ci # search onwards through words after 777db96d56Sopenharmony_ci for w in wafter: 787db96d56Sopenharmony_ci if dict.get(w): 797db96d56Sopenharmony_ci continue 807db96d56Sopenharmony_ci words.append(w) 817db96d56Sopenharmony_ci dict[w] = w 827db96d56Sopenharmony_ci words.append(word) 837db96d56Sopenharmony_ci return words 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci def getprevword(self): 867db96d56Sopenharmony_ci "Return the word prefix before the cursor." 877db96d56Sopenharmony_ci line = self.text.get("insert linestart", "insert") 887db96d56Sopenharmony_ci i = len(line) 897db96d56Sopenharmony_ci while i > 0 and line[i-1] in self.wordchars: 907db96d56Sopenharmony_ci i = i-1 917db96d56Sopenharmony_ci return line[i:] 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ciif __name__ == '__main__': 957db96d56Sopenharmony_ci from unittest import main 967db96d56Sopenharmony_ci main('idlelib.idle_test.test_autoexpand', verbosity=2) 97