blob: dc1ac8b5d2ef7dd96023ead4b79582b469282d8d [file] [log] [blame]
Terry Jan Reedyb638a382013-08-13 19:51:04 -04001"Implement Idle Shell history mechanism with History class"
2
Florent Xiclunad630c042010-04-02 07:24:52 +00003from idlelib.configHandler import idleConf
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +00004
David Scherer7aced172000-08-15 01:13:23 +00005class History:
Terry Jan Reedyb638a382013-08-13 19:51:04 -04006 ''' Implement Idle Shell history mechanism.
David Scherer7aced172000-08-15 01:13:23 +00007
Terry Jan Reedyb638a382013-08-13 19:51:04 -04008 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 '''
David Scherer7aced172000-08-15 01:13:23 +000013 def __init__(self, text, output_sep = "\n"):
Terry Jan Reedyb638a382013-08-13 19:51:04 -040014 '''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 '''
David Scherer7aced172000-08-15 01:13:23 +000022 self.text = text
23 self.history = []
Terry Jan Reedyb638a382013-08-13 19:51:04 -040024 self.prefix = None
25 self.pointer = None
David Scherer7aced172000-08-15 01:13:23 +000026 self.output_sep = output_sep
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 Reedyb638a382013-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 Reedyb638a382013-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
41 def _get_source(self, start, end):
42 # Get source code from start index to end index. Lines in the
43 # text control may be separated by sys.ps2 .
Kurt B. Kaiser6b06f292002-09-16 22:09:19 +000044 lines = self.text.get(start, end).split(self.output_sep)
45 return "\n".join(lines)
David Scherer7aced172000-08-15 01:13:23 +000046
47 def _put_source(self, where, source):
Kurt B. Kaiser6b06f292002-09-16 22:09:19 +000048 output = self.output_sep.join(source.split("\n"))
David Scherer7aced172000-08-15 01:13:23 +000049 self.text.insert(where, output)
50
Terry Jan Reedyb638a382013-08-13 19:51:04 -040051 def fetch(self, reverse):
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -040052 '''Fetch statememt and replace current line in text widget.
53
54 Set prefix and pointer as needed for successive fetches.
55 Reset them to None, None when returning to the start line.
56 Sound bell when return to start line or cannot leave a line
57 because cyclic is False.
58 '''
David Scherer7aced172000-08-15 01:13:23 +000059 nhist = len(self.history)
Terry Jan Reedyb638a382013-08-13 19:51:04 -040060 pointer = self.pointer
61 prefix = self.prefix
David Scherer7aced172000-08-15 01:13:23 +000062 if pointer is not None and prefix is not None:
63 if self.text.compare("insert", "!=", "end-1c") or \
64 self._get_source("iomark", "end-1c") != self.history[pointer]:
65 pointer = prefix = None
66 if pointer is None or prefix is None:
67 prefix = self._get_source("iomark", "end-1c")
68 if reverse:
Terry Jan Reedyb638a382013-08-13 19:51:04 -040069 pointer = nhist # will be decremented
David Scherer7aced172000-08-15 01:13:23 +000070 else:
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000071 if self.cyclic:
Terry Jan Reedyb638a382013-08-13 19:51:04 -040072 pointer = -1 # will be incremented
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -040073 else: # abort history_next
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000074 self.text.bell()
75 return
David Scherer7aced172000-08-15 01:13:23 +000076 nprefix = len(prefix)
77 while 1:
78 if reverse:
79 pointer = pointer - 1
80 else:
81 pointer = pointer + 1
82 if pointer < 0 or pointer >= nhist:
83 self.text.bell()
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -040084 if not self.cyclic and pointer < 0: # abort history_prev
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000085 return
86 else:
87 if self._get_source("iomark", "end-1c") != prefix:
88 self.text.delete("iomark", "end-1c")
89 self._put_source("iomark", prefix)
90 pointer = prefix = None
David Scherer7aced172000-08-15 01:13:23 +000091 break
92 item = self.history[pointer]
93 if item[:nprefix] == prefix and len(item) > nprefix:
94 self.text.delete("iomark", "end-1c")
95 self._put_source("iomark", item)
96 break
97 self.text.mark_set("insert", "end-1c")
98 self.text.see("insert")
99 self.text.tag_remove("sel", "1.0", "end")
Terry Jan Reedyb638a382013-08-13 19:51:04 -0400100 self.pointer = pointer
101 self.prefix = prefix
David Scherer7aced172000-08-15 01:13:23 +0000102
Terry Jan Reedyb638a382013-08-13 19:51:04 -0400103 def store(self, source):
104 "Store Shell input statement into history list."
Kurt B. Kaiser6b06f292002-09-16 22:09:19 +0000105 source = source.strip()
David Scherer7aced172000-08-15 01:13:23 +0000106 if len(source) > 2:
107 # avoid duplicates
108 try:
109 self.history.remove(source)
110 except ValueError:
111 pass
112 self.history.append(source)
Terry Jan Reedyb638a382013-08-13 19:51:04 -0400113 self.pointer = None
114 self.prefix = None
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -0400115
116if __name__ == "__main__":
117 from test import support
118 support.use_resources = ['gui']
119 from unittest import main
120 main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)