summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/IdleHistory.py
blob: 25fe6e4ad65ab868709f9975e67722b69a101bd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"Implement Idle Shell history mechanism with History class"

from idlelib.configHandler import idleConf

class History:
    ''' Implement Idle Shell history mechanism.

    store - Store source statement (called from PyShell.resetoutput).
    fetch - Fetch stored statement matching prefix already entered.
    history_next - Bound to <<history-next>> event (default Alt-N).
    history_prev - Bound to <<history-prev>> event (default Alt-P).
    '''
    def __init__(self, text, output_sep = "\n"):
        '''Initialize data attributes and bind event methods.

        .text - Idle wrapper of tk Text widget, with .bell().
        .history - source statements, possibly with multiple lines.
        .prefix - source already entered at prompt; filters history list.
        .pointer - index into history.
        .cyclic - wrap around history list (or not).
        '''
        self.text = text
        self.history = []
        self.prefix = None
        self.pointer = None
        self.output_sep = output_sep
        self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
        text.bind("<<history-previous>>", self.history_prev)
        text.bind("<<history-next>>", self.history_next)

    def history_next(self, event):
        "Fetch later statement; start with ealiest if cyclic."
        self.fetch(reverse=False)
        return "break"

    def history_prev(self, event):
        "Fetch earlier statement; start with most recent."
        self.fetch(reverse=True)
        return "break"

    def _get_source(self, start, end):
        # Get source code from start index to end index.  Lines in the
        # text control may be separated by sys.ps2 .
        lines = self.text.get(start, end).split(self.output_sep)
        return "\n".join(lines)

    def _put_source(self, where, source):
        output = self.output_sep.join(source.split("\n"))
        self.text.insert(where, output)

    def fetch(self, reverse):
        "Fetch statememt and enter into text at cursor."
        nhist = len(self.history)
        pointer = self.pointer
        prefix = self.prefix
        if pointer is not None and prefix is not None:
            if self.text.compare("insert", "!=", "end-1c") or \
               self._get_source("iomark", "end-1c") != self.history[pointer]:
                pointer = prefix = None
        if pointer is None or prefix is None:
            prefix = self._get_source("iomark", "end-1c")
            if reverse:
                pointer = nhist  # will be decremented
            else:
                if self.cyclic:
                    pointer = -1  # will be incremented
                else:
                    self.text.bell()
                    return
        nprefix = len(prefix)
        while 1:
            if reverse:
                pointer = pointer - 1
            else:
                pointer = pointer + 1
            if pointer < 0 or pointer >= nhist:
                self.text.bell()
                if not self.cyclic and pointer < 0:
                    return
                else:
                    if self._get_source("iomark", "end-1c") != prefix:
                        self.text.delete("iomark", "end-1c")
                        self._put_source("iomark", prefix)
                    pointer = prefix = None
                break
            item = self.history[pointer]
            if item[:nprefix] == prefix and len(item) > nprefix:
                self.text.delete("iomark", "end-1c")
                self._put_source("iomark", item)
                break
        self.text.mark_set("insert", "end-1c")
        self.text.see("insert")
        self.text.tag_remove("sel", "1.0", "end")
        self.pointer = pointer
        self.prefix = prefix

    def store(self, source):
        "Store Shell input statement into history list."
        source = source.strip()
        if len(source) > 2:
            # avoid duplicates
            try:
                self.history.remove(source)
            except ValueError:
                pass
            self.history.append(source)
        self.pointer = None
        self.prefix = None