17db96d56Sopenharmony_ci"Implement Idle Shell history mechanism with History class" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_cifrom idlelib.config import idleConf 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ciclass History: 77db96d56Sopenharmony_ci ''' Implement Idle Shell history mechanism. 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_ci store - Store source statement (called from pyshell.resetoutput). 107db96d56Sopenharmony_ci fetch - Fetch stored statement matching prefix already entered. 117db96d56Sopenharmony_ci history_next - Bound to <<history-next>> event (default Alt-N). 127db96d56Sopenharmony_ci history_prev - Bound to <<history-prev>> event (default Alt-P). 137db96d56Sopenharmony_ci ''' 147db96d56Sopenharmony_ci def __init__(self, text): 157db96d56Sopenharmony_ci '''Initialize data attributes and bind event methods. 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci .text - Idle wrapper of tk Text widget, with .bell(). 187db96d56Sopenharmony_ci .history - source statements, possibly with multiple lines. 197db96d56Sopenharmony_ci .prefix - source already entered at prompt; filters history list. 207db96d56Sopenharmony_ci .pointer - index into history. 217db96d56Sopenharmony_ci .cyclic - wrap around history list (or not). 227db96d56Sopenharmony_ci ''' 237db96d56Sopenharmony_ci self.text = text 247db96d56Sopenharmony_ci self.history = [] 257db96d56Sopenharmony_ci self.prefix = None 267db96d56Sopenharmony_ci self.pointer = None 277db96d56Sopenharmony_ci self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool") 287db96d56Sopenharmony_ci text.bind("<<history-previous>>", self.history_prev) 297db96d56Sopenharmony_ci text.bind("<<history-next>>", self.history_next) 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ci def history_next(self, event): 327db96d56Sopenharmony_ci "Fetch later statement; start with earliest if cyclic." 337db96d56Sopenharmony_ci self.fetch(reverse=False) 347db96d56Sopenharmony_ci return "break" 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci def history_prev(self, event): 377db96d56Sopenharmony_ci "Fetch earlier statement; start with most recent." 387db96d56Sopenharmony_ci self.fetch(reverse=True) 397db96d56Sopenharmony_ci return "break" 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci def fetch(self, reverse): 427db96d56Sopenharmony_ci '''Fetch statement and replace current line in text widget. 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci Set prefix and pointer as needed for successive fetches. 457db96d56Sopenharmony_ci Reset them to None, None when returning to the start line. 467db96d56Sopenharmony_ci Sound bell when return to start line or cannot leave a line 477db96d56Sopenharmony_ci because cyclic is False. 487db96d56Sopenharmony_ci ''' 497db96d56Sopenharmony_ci nhist = len(self.history) 507db96d56Sopenharmony_ci pointer = self.pointer 517db96d56Sopenharmony_ci prefix = self.prefix 527db96d56Sopenharmony_ci if pointer is not None and prefix is not None: 537db96d56Sopenharmony_ci if self.text.compare("insert", "!=", "end-1c") or \ 547db96d56Sopenharmony_ci self.text.get("iomark", "end-1c") != self.history[pointer]: 557db96d56Sopenharmony_ci pointer = prefix = None 567db96d56Sopenharmony_ci self.text.mark_set("insert", "end-1c") # != after cursor move 577db96d56Sopenharmony_ci if pointer is None or prefix is None: 587db96d56Sopenharmony_ci prefix = self.text.get("iomark", "end-1c") 597db96d56Sopenharmony_ci if reverse: 607db96d56Sopenharmony_ci pointer = nhist # will be decremented 617db96d56Sopenharmony_ci else: 627db96d56Sopenharmony_ci if self.cyclic: 637db96d56Sopenharmony_ci pointer = -1 # will be incremented 647db96d56Sopenharmony_ci else: # abort history_next 657db96d56Sopenharmony_ci self.text.bell() 667db96d56Sopenharmony_ci return 677db96d56Sopenharmony_ci nprefix = len(prefix) 687db96d56Sopenharmony_ci while True: 697db96d56Sopenharmony_ci pointer += -1 if reverse else 1 707db96d56Sopenharmony_ci if pointer < 0 or pointer >= nhist: 717db96d56Sopenharmony_ci self.text.bell() 727db96d56Sopenharmony_ci if not self.cyclic and pointer < 0: # abort history_prev 737db96d56Sopenharmony_ci return 747db96d56Sopenharmony_ci else: 757db96d56Sopenharmony_ci if self.text.get("iomark", "end-1c") != prefix: 767db96d56Sopenharmony_ci self.text.delete("iomark", "end-1c") 777db96d56Sopenharmony_ci self.text.insert("iomark", prefix, "stdin") 787db96d56Sopenharmony_ci pointer = prefix = None 797db96d56Sopenharmony_ci break 807db96d56Sopenharmony_ci item = self.history[pointer] 817db96d56Sopenharmony_ci if item[:nprefix] == prefix and len(item) > nprefix: 827db96d56Sopenharmony_ci self.text.delete("iomark", "end-1c") 837db96d56Sopenharmony_ci self.text.insert("iomark", item, "stdin") 847db96d56Sopenharmony_ci break 857db96d56Sopenharmony_ci self.text.see("insert") 867db96d56Sopenharmony_ci self.text.tag_remove("sel", "1.0", "end") 877db96d56Sopenharmony_ci self.pointer = pointer 887db96d56Sopenharmony_ci self.prefix = prefix 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci def store(self, source): 917db96d56Sopenharmony_ci "Store Shell input statement into history list." 927db96d56Sopenharmony_ci source = source.strip() 937db96d56Sopenharmony_ci if len(source) > 2: 947db96d56Sopenharmony_ci # avoid duplicates 957db96d56Sopenharmony_ci try: 967db96d56Sopenharmony_ci self.history.remove(source) 977db96d56Sopenharmony_ci except ValueError: 987db96d56Sopenharmony_ci pass 997db96d56Sopenharmony_ci self.history.append(source) 1007db96d56Sopenharmony_ci self.pointer = None 1017db96d56Sopenharmony_ci self.prefix = None 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ciif __name__ == "__main__": 1057db96d56Sopenharmony_ci from unittest import main 1067db96d56Sopenharmony_ci main('idlelib.idle_test.test_history', verbosity=2, exit=False) 107