This file is indexed.

/usr/lib/python2.7/dist-packages/meld/undo.py is in meld 3.14.2-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org>
# Copyright (C) 2010-2011 Kai Willadsen <kai.willadsen@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Module to help implement undo functionality.

Usage:

t = TextWidget()
s = UndoSequence()
def on_textwidget_text_inserted():
    s.begin_group()
    if not t.is_modified():
        s.add_action( TextWidgetModifiedAction() )
    s.add_action( InsertionAction() )
    s.end_group()

def on_undo_button_pressed():
    s.undo()
"""

from gi.repository import GObject


class GroupAction(object):
    """A group action combines several actions into one logical action.
    """
    def __init__(self, seq):
        self.seq = seq
        # TODO: If a GroupAction affects more than one sequence, our logic
        # breaks. Currently, this isn't a problem.
        self.buffer = seq.actions[0].buffer

    def undo(self):
        while self.seq.can_undo():
            self.seq.undo()

    def redo(self):
        while self.seq.can_redo():
            self.seq.redo()


class UndoSequence(GObject.GObject):
    """A manager class for operations which can be undone/redone.
    """

    __gsignals__ = {
        'can-undo': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_BOOLEAN,)),
        'can-redo': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_BOOLEAN,)),
        'checkpointed': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_OBJECT, GObject.TYPE_BOOLEAN,)),
    }

    def __init__(self):
        """Create an empty UndoSequence.
        """
        GObject.GObject.__init__(self)
        self.actions = []
        self.next_redo = 0
        self.checkpoints = {}
        self.group = None
        self.busy = False

    def clear(self):
        """Remove all undo and redo actions from this sequence

        If the sequence was previously able to undo and/or redo, the
        'can-undo' and 'can-redo' signals are emitted.

        Raises an AssertionError if a group is in progress.
        """
        assert self.group is None
        if self.can_undo():
            self.emit('can-undo', 0)
        if self.can_redo():
            self.emit('can-redo', 0)
        self.actions = []
        self.next_redo = 0
        self.checkpoints = {}

    def can_undo(self):
        """Return if an undo is possible.
        """
        return self.next_redo > 0

    def can_redo(self):
        """Return if a redo is possible.
        """
        return self.next_redo < len(self.actions)

    def add_action(self, action):
        """Add an action to the undo list.

        Arguments:

        action -- A class with two callable attributes: 'undo' and 'redo'
                  which are called by this sequence during an undo or redo.
        """
        if self.busy:
            return

        if self.group is None:
            if self.checkpointed(action.buffer):
                self.checkpoints[action.buffer][1] = self.next_redo
                self.emit('checkpointed', action.buffer, False)
            else:
                # If we go back in the undo stack before the checkpoint starts,
                # and then modify the buffer, we lose the checkpoint altogether
                start, end = self.checkpoints.get(action.buffer, (None, None))
                if start is not None and start > self.next_redo:
                    self.checkpoints[action.buffer] = (None, None)
            could_undo = self.can_undo()
            could_redo = self.can_redo()
            self.actions[self.next_redo:] = []
            self.actions.append(action)
            self.next_redo += 1
            if not could_undo:
                self.emit('can-undo', 1)
            if could_redo:
                self.emit('can-redo', 0)
        else:
            self.group.add_action(action)

    def undo(self):
        """Undo an action.

        Raises an AssertionError if the sequence is not undoable.
        """
        assert self.next_redo > 0
        self.busy = True
        buf = self.actions[self.next_redo - 1].buffer
        if self.checkpointed(buf):
            self.emit('checkpointed', buf, False)
        could_redo = self.can_redo()
        self.next_redo -= 1
        self.actions[self.next_redo].undo()
        self.busy = False
        if not self.can_undo():
            self.emit('can-undo', 0)
        if not could_redo:
            self.emit('can-redo', 1)
        if self.checkpointed(buf):
            self.emit('checkpointed', buf, True)

    def redo(self):
        """Redo an action.

        Raises and AssertionError if the sequence is not undoable.
        """
        assert self.next_redo < len(self.actions)
        self.busy = True
        buf = self.actions[self.next_redo].buffer
        if self.checkpointed(buf):
            self.emit('checkpointed', buf, False)
        could_undo = self.can_undo()
        a = self.actions[self.next_redo]
        self.next_redo += 1
        a.redo()
        self.busy = False
        if not could_undo:
            self.emit('can-undo', 1)
        if not self.can_redo():
            self.emit('can-redo', 0)
        if self.checkpointed(buf):
            self.emit('checkpointed', buf, True)

    def checkpoint(self, buf):
        start = self.next_redo
        while start > 0 and self.actions[start - 1].buffer != buf:
            start -= 1
        end = self.next_redo
        while (end < len(self.actions) - 1 and
               self.actions[end + 1].buffer != buf):
            end += 1
        if end == len(self.actions):
            end = None
        self.checkpoints[buf] = [start, end]
        self.emit('checkpointed', buf, True)

    def checkpointed(self, buf):
        # While the main undo sequence should always have checkpoints
        # recorded, grouped subsequences won't.
        start, end = self.checkpoints.get(buf, (None, None))
        if start is None:
            return False
        if end is None:
            end = len(self.actions)
        return start <= self.next_redo <= end

    def begin_group(self):
        """Group several actions into a single logical action.

        When you wrap several calls to add_action() inside begin_group()
        and end_group(), all the intervening actions are considered
        one logical action. For instance a 'replace' action may be
        implemented as a pair of 'delete' and 'create' actions, but
        undoing should undo both of them.
        """
        if self.busy:
            return

        if self.group:
            self.group.begin_group()
        else:
            self.group = UndoSequence()

    def end_group(self):
        """End a logical group action. See also begin_group().

        Raises an AssertionError if there was not a matching call to
        begin_group().
        """
        if self.busy:
            return

        assert self.group is not None
        if self.group.group is not None:
            self.group.end_group()
        else:
            group = self.group
            self.group = None
            # Collapse single action groups
            if len(group.actions) == 1:
                self.add_action(group.actions[0])
            elif len(group.actions) > 1:
                self.add_action(GroupAction(group))

    def abort_group(self):
        """Revert the sequence to the state before begin_group() was called.

        Raises an AssertionError if there was no a matching call to begin_group().
        """
        if self.busy:
            return

        assert self.group is not None
        if self.group.group is not None:
            self.group.abort_group()
        else:
            self.group = None

    def in_grouped_action(self):
        return self.group is not None