jpayne@68
|
1 "Implement Idle Shell history mechanism with History class"
|
jpayne@68
|
2
|
jpayne@68
|
3 from idlelib.config import idleConf
|
jpayne@68
|
4
|
jpayne@68
|
5
|
jpayne@68
|
6 class History:
|
jpayne@68
|
7 ''' Implement Idle Shell history mechanism.
|
jpayne@68
|
8
|
jpayne@68
|
9 store - Store source statement (called from pyshell.resetoutput).
|
jpayne@68
|
10 fetch - Fetch stored statement matching prefix already entered.
|
jpayne@68
|
11 history_next - Bound to <<history-next>> event (default Alt-N).
|
jpayne@68
|
12 history_prev - Bound to <<history-prev>> event (default Alt-P).
|
jpayne@68
|
13 '''
|
jpayne@68
|
14 def __init__(self, text):
|
jpayne@68
|
15 '''Initialize data attributes and bind event methods.
|
jpayne@68
|
16
|
jpayne@68
|
17 .text - Idle wrapper of tk Text widget, with .bell().
|
jpayne@68
|
18 .history - source statements, possibly with multiple lines.
|
jpayne@68
|
19 .prefix - source already entered at prompt; filters history list.
|
jpayne@68
|
20 .pointer - index into history.
|
jpayne@68
|
21 .cyclic - wrap around history list (or not).
|
jpayne@68
|
22 '''
|
jpayne@68
|
23 self.text = text
|
jpayne@68
|
24 self.history = []
|
jpayne@68
|
25 self.prefix = None
|
jpayne@68
|
26 self.pointer = None
|
jpayne@68
|
27 self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
|
jpayne@68
|
28 text.bind("<<history-previous>>", self.history_prev)
|
jpayne@68
|
29 text.bind("<<history-next>>", self.history_next)
|
jpayne@68
|
30
|
jpayne@68
|
31 def history_next(self, event):
|
jpayne@68
|
32 "Fetch later statement; start with ealiest if cyclic."
|
jpayne@68
|
33 self.fetch(reverse=False)
|
jpayne@68
|
34 return "break"
|
jpayne@68
|
35
|
jpayne@68
|
36 def history_prev(self, event):
|
jpayne@68
|
37 "Fetch earlier statement; start with most recent."
|
jpayne@68
|
38 self.fetch(reverse=True)
|
jpayne@68
|
39 return "break"
|
jpayne@68
|
40
|
jpayne@68
|
41 def fetch(self, reverse):
|
jpayne@68
|
42 '''Fetch statement and replace current line in text widget.
|
jpayne@68
|
43
|
jpayne@68
|
44 Set prefix and pointer as needed for successive fetches.
|
jpayne@68
|
45 Reset them to None, None when returning to the start line.
|
jpayne@68
|
46 Sound bell when return to start line or cannot leave a line
|
jpayne@68
|
47 because cyclic is False.
|
jpayne@68
|
48 '''
|
jpayne@68
|
49 nhist = len(self.history)
|
jpayne@68
|
50 pointer = self.pointer
|
jpayne@68
|
51 prefix = self.prefix
|
jpayne@68
|
52 if pointer is not None and prefix is not None:
|
jpayne@68
|
53 if self.text.compare("insert", "!=", "end-1c") or \
|
jpayne@68
|
54 self.text.get("iomark", "end-1c") != self.history[pointer]:
|
jpayne@68
|
55 pointer = prefix = None
|
jpayne@68
|
56 self.text.mark_set("insert", "end-1c") # != after cursor move
|
jpayne@68
|
57 if pointer is None or prefix is None:
|
jpayne@68
|
58 prefix = self.text.get("iomark", "end-1c")
|
jpayne@68
|
59 if reverse:
|
jpayne@68
|
60 pointer = nhist # will be decremented
|
jpayne@68
|
61 else:
|
jpayne@68
|
62 if self.cyclic:
|
jpayne@68
|
63 pointer = -1 # will be incremented
|
jpayne@68
|
64 else: # abort history_next
|
jpayne@68
|
65 self.text.bell()
|
jpayne@68
|
66 return
|
jpayne@68
|
67 nprefix = len(prefix)
|
jpayne@68
|
68 while 1:
|
jpayne@68
|
69 pointer += -1 if reverse else 1
|
jpayne@68
|
70 if pointer < 0 or pointer >= nhist:
|
jpayne@68
|
71 self.text.bell()
|
jpayne@68
|
72 if not self.cyclic and pointer < 0: # abort history_prev
|
jpayne@68
|
73 return
|
jpayne@68
|
74 else:
|
jpayne@68
|
75 if self.text.get("iomark", "end-1c") != prefix:
|
jpayne@68
|
76 self.text.delete("iomark", "end-1c")
|
jpayne@68
|
77 self.text.insert("iomark", prefix)
|
jpayne@68
|
78 pointer = prefix = None
|
jpayne@68
|
79 break
|
jpayne@68
|
80 item = self.history[pointer]
|
jpayne@68
|
81 if item[:nprefix] == prefix and len(item) > nprefix:
|
jpayne@68
|
82 self.text.delete("iomark", "end-1c")
|
jpayne@68
|
83 self.text.insert("iomark", item)
|
jpayne@68
|
84 break
|
jpayne@68
|
85 self.text.see("insert")
|
jpayne@68
|
86 self.text.tag_remove("sel", "1.0", "end")
|
jpayne@68
|
87 self.pointer = pointer
|
jpayne@68
|
88 self.prefix = prefix
|
jpayne@68
|
89
|
jpayne@68
|
90 def store(self, source):
|
jpayne@68
|
91 "Store Shell input statement into history list."
|
jpayne@68
|
92 source = source.strip()
|
jpayne@68
|
93 if len(source) > 2:
|
jpayne@68
|
94 # avoid duplicates
|
jpayne@68
|
95 try:
|
jpayne@68
|
96 self.history.remove(source)
|
jpayne@68
|
97 except ValueError:
|
jpayne@68
|
98 pass
|
jpayne@68
|
99 self.history.append(source)
|
jpayne@68
|
100 self.pointer = None
|
jpayne@68
|
101 self.prefix = None
|
jpayne@68
|
102
|
jpayne@68
|
103
|
jpayne@68
|
104 if __name__ == "__main__":
|
jpayne@68
|
105 from unittest import main
|
jpayne@68
|
106 main('idlelib.idle_test.test_history', verbosity=2, exit=False)
|