blob: 56f53a0f2fb9912925b3c566aab9d412c8864d95 [file] [log] [blame]
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -04001"Implement Idle Shell history mechanism with History class"
2
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -04003from idlelib.config import idleConf
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +00004
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -04005
David Scherer7aced172000-08-15 01:13:23 +00006class History:
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -04007 ''' Implement Idle Shell history mechanism.
David Scherer7aced172000-08-15 01:13:23 +00008
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -04009 store - Store source statement (called from pyshell.resetoutput).
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040010 fetch - Fetch stored statement matching prefix already entered.
11 history_next - Bound to <<history-next>> event (default Alt-N).
12 history_prev - Bound to <<history-prev>> event (default Alt-P).
13 '''
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040014 def __init__(self, text):
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040015 '''Initialize data attributes and bind event methods.
16
17 .text - Idle wrapper of tk Text widget, with .bell().
18 .history - source statements, possibly with multiple lines.
19 .prefix - source already entered at prompt; filters history list.
20 .pointer - index into history.
21 .cyclic - wrap around history list (or not).
22 '''
David Scherer7aced172000-08-15 01:13:23 +000023 self.text = text
24 self.history = []
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040025 self.prefix = None
26 self.pointer = None
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000027 self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
David Scherer7aced172000-08-15 01:13:23 +000028 text.bind("<<history-previous>>", self.history_prev)
29 text.bind("<<history-next>>", self.history_next)
30
31 def history_next(self, event):
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040032 "Fetch later statement; start with ealiest if cyclic."
33 self.fetch(reverse=False)
David Scherer7aced172000-08-15 01:13:23 +000034 return "break"
35
36 def history_prev(self, event):
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040037 "Fetch earlier statement; start with most recent."
38 self.fetch(reverse=True)
David Scherer7aced172000-08-15 01:13:23 +000039 return "break"
40
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040041 def fetch(self, reverse):
Terry Jan Reedyc2564f92013-08-15 14:32:04 -040042 '''Fetch statememt and replace current line in text widget.
43
44 Set prefix and pointer as needed for successive fetches.
45 Reset them to None, None when returning to the start line.
46 Sound bell when return to start line or cannot leave a line
47 because cyclic is False.
48 '''
David Scherer7aced172000-08-15 01:13:23 +000049 nhist = len(self.history)
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040050 pointer = self.pointer
51 prefix = self.prefix
David Scherer7aced172000-08-15 01:13:23 +000052 if pointer is not None and prefix is not None:
53 if self.text.compare("insert", "!=", "end-1c") or \
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040054 self.text.get("iomark", "end-1c") != self.history[pointer]:
David Scherer7aced172000-08-15 01:13:23 +000055 pointer = prefix = None
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040056 self.text.mark_set("insert", "end-1c") # != after cursor move
David Scherer7aced172000-08-15 01:13:23 +000057 if pointer is None or prefix is None:
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040058 prefix = self.text.get("iomark", "end-1c")
David Scherer7aced172000-08-15 01:13:23 +000059 if reverse:
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040060 pointer = nhist # will be decremented
David Scherer7aced172000-08-15 01:13:23 +000061 else:
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000062 if self.cyclic:
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040063 pointer = -1 # will be incremented
Terry Jan Reedyc2564f92013-08-15 14:32:04 -040064 else: # abort history_next
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000065 self.text.bell()
66 return
David Scherer7aced172000-08-15 01:13:23 +000067 nprefix = len(prefix)
68 while 1:
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040069 pointer += -1 if reverse else 1
David Scherer7aced172000-08-15 01:13:23 +000070 if pointer < 0 or pointer >= nhist:
71 self.text.bell()
Terry Jan Reedyc2564f92013-08-15 14:32:04 -040072 if not self.cyclic and pointer < 0: # abort history_prev
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000073 return
74 else:
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040075 if self.text.get("iomark", "end-1c") != prefix:
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000076 self.text.delete("iomark", "end-1c")
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040077 self.text.insert("iomark", prefix)
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000078 pointer = prefix = None
David Scherer7aced172000-08-15 01:13:23 +000079 break
80 item = self.history[pointer]
81 if item[:nprefix] == prefix and len(item) > nprefix:
82 self.text.delete("iomark", "end-1c")
Terry Jan Reedy29c3fc52013-08-15 16:19:13 -040083 self.text.insert("iomark", item)
David Scherer7aced172000-08-15 01:13:23 +000084 break
David Scherer7aced172000-08-15 01:13:23 +000085 self.text.see("insert")
86 self.text.tag_remove("sel", "1.0", "end")
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040087 self.pointer = pointer
88 self.prefix = prefix
David Scherer7aced172000-08-15 01:13:23 +000089
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -040090 def store(self, source):
91 "Store Shell input statement into history list."
Kurt B. Kaiser6b06f292002-09-16 22:09:19 +000092 source = source.strip()
David Scherer7aced172000-08-15 01:13:23 +000093 if len(source) > 2:
94 # avoid duplicates
95 try:
96 self.history.remove(source)
97 except ValueError:
98 pass
99 self.history.append(source)
Terry Jan Reedy0a01ac42013-08-13 19:51:04 -0400100 self.pointer = None
101 self.prefix = None
Terry Jan Reedyc2564f92013-08-15 14:32:04 -0400102
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400103
Terry Jan Reedyc2564f92013-08-15 14:32:04 -0400104if __name__ == "__main__":
Terry Jan Reedyc2564f92013-08-15 14:32:04 -0400105 from unittest import main
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400106 main('idlelib.idle_test.test_history', verbosity=2, exit=False)