xref: /third_party/python/Lib/idlelib/history.py (revision 7db96d56)
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