97 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| '''Complete the current word before the cursor with words in the editor.
 | |
| 
 | |
| Each menu selection or shortcut key selection replaces the word with a
 | |
| different word with the same prefix. The search for matches begins
 | |
| before the target and moves toward the top of the editor. It then starts
 | |
| after the cursor and moves down. It then returns to the original word and
 | |
| the cycle starts again.
 | |
| 
 | |
| Changing the current text line or leaving the cursor in a different
 | |
| place before requesting the next selection causes AutoExpand to reset
 | |
| its state.
 | |
| 
 | |
| There is only one instance of Autoexpand.
 | |
| '''
 | |
| import re
 | |
| import string
 | |
| 
 | |
| 
 | |
| class AutoExpand:
 | |
|     wordchars = string.ascii_letters + string.digits + "_"
 | |
| 
 | |
|     def __init__(self, editwin):
 | |
|         self.text = editwin.text
 | |
|         self.bell = self.text.bell
 | |
|         self.state = None
 | |
| 
 | |
|     def expand_word_event(self, event):
 | |
|         "Replace the current word with the next expansion."
 | |
|         curinsert = self.text.index("insert")
 | |
|         curline = self.text.get("insert linestart", "insert lineend")
 | |
|         if not self.state:
 | |
|             words = self.getwords()
 | |
|             index = 0
 | |
|         else:
 | |
|             words, index, insert, line = self.state
 | |
|             if insert != curinsert or line != curline:
 | |
|                 words = self.getwords()
 | |
|                 index = 0
 | |
|         if not words:
 | |
|             self.bell()
 | |
|             return "break"
 | |
|         word = self.getprevword()
 | |
|         self.text.delete("insert - %d chars" % len(word), "insert")
 | |
|         newword = words[index]
 | |
|         index = (index + 1) % len(words)
 | |
|         if index == 0:
 | |
|             self.bell()            # Warn we cycled around
 | |
|         self.text.insert("insert", newword)
 | |
|         curinsert = self.text.index("insert")
 | |
|         curline = self.text.get("insert linestart", "insert lineend")
 | |
|         self.state = words, index, curinsert, curline
 | |
|         return "break"
 | |
| 
 | |
|     def getwords(self):
 | |
|         "Return a list of words that match the prefix before the cursor."
 | |
|         word = self.getprevword()
 | |
|         if not word:
 | |
|             return []
 | |
|         before = self.text.get("1.0", "insert wordstart")
 | |
|         wbefore = re.findall(r"\b" + word + r"\w+\b", before)
 | |
|         del before
 | |
|         after = self.text.get("insert wordend", "end")
 | |
|         wafter = re.findall(r"\b" + word + r"\w+\b", after)
 | |
|         del after
 | |
|         if not wbefore and not wafter:
 | |
|             return []
 | |
|         words = []
 | |
|         dict = {}
 | |
|         # search backwards through words before
 | |
|         wbefore.reverse()
 | |
|         for w in wbefore:
 | |
|             if dict.get(w):
 | |
|                 continue
 | |
|             words.append(w)
 | |
|             dict[w] = w
 | |
|         # search onwards through words after
 | |
|         for w in wafter:
 | |
|             if dict.get(w):
 | |
|                 continue
 | |
|             words.append(w)
 | |
|             dict[w] = w
 | |
|         words.append(word)
 | |
|         return words
 | |
| 
 | |
|     def getprevword(self):
 | |
|         "Return the word prefix before the cursor."
 | |
|         line = self.text.get("insert linestart", "insert")
 | |
|         i = len(line)
 | |
|         while i > 0 and line[i-1] in self.wordchars:
 | |
|             i = i-1
 | |
|         return line[i:]
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     from unittest import main
 | |
|     main('idlelib.idle_test.test_autoexpand', verbosity=2)
 |