blob: 078af2905325b5df1912ea345669f513ff9c6a5e [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 '''
Terry Jan Reedy1703df62013-08-15 16:19:07 -040013 def __init__(self, text):
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
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000026 self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
David Scherer7aced172000-08-15 01:13:23 +000027 text.bind("<<history-previous>>", self.history_prev)
28 text.bind("<<history-next>>", self.history_next)
29
30 def history_next(self, event):
Terry Jan Reedyb638a382013-08-13 19:51:04 -040031 "Fetch later statement; start with ealiest if cyclic."
32 self.fetch(reverse=False)
David Scherer7aced172000-08-15 01:13:23 +000033 return "break"
34
35 def history_prev(self, event):
Terry Jan Reedyb638a382013-08-13 19:51:04 -040036 "Fetch earlier statement; start with most recent."
37 self.fetch(reverse=True)
David Scherer7aced172000-08-15 01:13:23 +000038 return "break"
39
Terry Jan Reedyb638a382013-08-13 19:51:04 -040040 def fetch(self, reverse):
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -040041 '''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 '''
David Scherer7aced172000-08-15 01:13:23 +000048 nhist = len(self.history)
Terry Jan Reedyb638a382013-08-13 19:51:04 -040049 pointer = self.pointer
50 prefix = self.prefix
David Scherer7aced172000-08-15 01:13:23 +000051 if pointer is not None and prefix is not None:
52 if self.text.compare("insert", "!=", "end-1c") or \
Terry Jan Reedy1703df62013-08-15 16:19:07 -040053 self.text.get("iomark", "end-1c") != self.history[pointer]:
David Scherer7aced172000-08-15 01:13:23 +000054 pointer = prefix = None
Terry Jan Reedy1703df62013-08-15 16:19:07 -040055 self.text.mark_set("insert", "end-1c") # != after cursor move
David Scherer7aced172000-08-15 01:13:23 +000056 if pointer is None or prefix is None:
Terry Jan Reedy1703df62013-08-15 16:19:07 -040057 prefix = self.text.get("iomark", "end-1c")
David Scherer7aced172000-08-15 01:13:23 +000058 if reverse:
Terry Jan Reedyb638a382013-08-13 19:51:04 -040059 pointer = nhist # will be decremented
David Scherer7aced172000-08-15 01:13:23 +000060 else:
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000061 if self.cyclic:
Terry Jan Reedyb638a382013-08-13 19:51:04 -040062 pointer = -1 # will be incremented
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -040063 else: # abort history_next
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000064 self.text.bell()
65 return
David Scherer7aced172000-08-15 01:13:23 +000066 nprefix = len(prefix)
67 while 1:
Terry Jan Reedy1703df62013-08-15 16:19:07 -040068 pointer += -1 if reverse else 1
David Scherer7aced172000-08-15 01:13:23 +000069 if pointer < 0 or pointer >= nhist:
70 self.text.bell()
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -040071 if not self.cyclic and pointer < 0: # abort history_prev
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000072 return
73 else:
Terry Jan Reedy1703df62013-08-15 16:19:07 -040074 if self.text.get("iomark", "end-1c") != prefix:
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000075 self.text.delete("iomark", "end-1c")
Terry Jan Reedy1703df62013-08-15 16:19:07 -040076 self.text.insert("iomark", prefix)
Kurt B. Kaiser0676dfd2005-02-03 01:37:14 +000077 pointer = prefix = None
David Scherer7aced172000-08-15 01:13:23 +000078 break
79 item = self.history[pointer]
80 if item[:nprefix] == prefix and len(item) > nprefix:
81 self.text.delete("iomark", "end-1c")
Terry Jan Reedy1703df62013-08-15 16:19:07 -040082 self.text.insert("iomark", item)
David Scherer7aced172000-08-15 01:13:23 +000083 break
David Scherer7aced172000-08-15 01:13:23 +000084 self.text.see("insert")
85 self.text.tag_remove("sel", "1.0", "end")
Terry Jan Reedyb638a382013-08-13 19:51:04 -040086 self.pointer = pointer
87 self.prefix = prefix
David Scherer7aced172000-08-15 01:13:23 +000088
Terry Jan Reedyb638a382013-08-13 19:51:04 -040089 def store(self, source):
90 "Store Shell input statement into history list."
Kurt B. Kaiser6b06f292002-09-16 22:09:19 +000091 source = source.strip()
David Scherer7aced172000-08-15 01:13:23 +000092 if len(source) > 2:
93 # avoid duplicates
94 try:
95 self.history.remove(source)
96 except ValueError:
97 pass
98 self.history.append(source)
Terry Jan Reedyb638a382013-08-13 19:51:04 -040099 self.pointer = None
100 self.prefix = None
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -0400101
102if __name__ == "__main__":
Terry Jan Reedy8ce36dd2013-08-15 14:31:55 -0400103 from unittest import main
104 main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)