Home | History | Annotate | Download | only in idlelib
      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 unittest import main
    104     main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)
    105