blob: 25fe6e4ad65ab868709f9975e67722b69a101bd1 [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):
52 "Fetch statememt and enter into text at cursor."
David Scherer7aced172000-08-15 01:13:23 +000053 nhist = len(self.history)
Terry Jan Reedyb638a382013-08-13 19:51:04 -040054 pointer = self.pointer
55 prefix = self.prefix
David Scherer7aced172000-08-15 01:13:23 +000056 if pointer is not None and prefix is not None:
57 if self.text.compare("insert", "!=", "end-1c") or \
58 self._get_source("iomark", "end-1c") != self.history[pointer]:
59 pointer = prefix = None
60 if pointer is None or prefix is None:
61 prefix = self._get_source("iomark", "end-1c")
62 if reverse:
Terry Jan Reedyb638a382013-08-13 19:51:04 -040063 pointer = nhist # will be decremented
David Scherer7aced172000-08-15 01:13:23 +000064 else:
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000065 if self.cyclic:
Terry Jan Reedyb638a382013-08-13 19:51:04 -040066 pointer = -1 # will be incremented
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000067 else:
68 self.text.bell()
69 return
David Scherer7aced172000-08-15 01:13:23 +000070 nprefix = len(prefix)
71 while 1:
72 if reverse:
73 pointer = pointer - 1
74 else:
75 pointer = pointer + 1
76 if pointer < 0 or pointer >= nhist:
77 self.text.bell()
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000078 if not self.cyclic and pointer < 0:
79 return
80 else:
81 if self._get_source("iomark", "end-1c") != prefix:
82 self.text.delete("iomark", "end-1c")
83 self._put_source("iomark", prefix)
84 pointer = prefix = None
David Scherer7aced172000-08-15 01:13:23 +000085 break
86 item = self.history[pointer]
87 if item[:nprefix] == prefix and len(item) > nprefix:
88 self.text.delete("iomark", "end-1c")
89 self._put_source("iomark", item)
90 break
91 self.text.mark_set("insert", "end-1c")
92 self.text.see("insert")
93 self.text.tag_remove("sel", "1.0", "end")
Terry Jan Reedyb638a382013-08-13 19:51:04 -040094 self.pointer = pointer
95 self.prefix = prefix
David Scherer7aced172000-08-15 01:13:23 +000096
Terry Jan Reedyb638a382013-08-13 19:51:04 -040097 def store(self, source):
98 "Store Shell input statement into history list."
Kurt B. Kaiser6b06f292002-09-16 22:09:19 +000099 source = source.strip()
David Scherer7aced172000-08-15 01:13:23 +0000100 if len(source) > 2:
101 # avoid duplicates
102 try:
103 self.history.remove(source)
104 except ValueError:
105 pass
106 self.history.append(source)
Terry Jan Reedyb638a382013-08-13 19:51:04 -0400107 self.pointer = None
108 self.prefix = None