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