/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to diff.py

  • Committer: Jelmer Vernooij
  • Date: 2012-07-09 15:23:26 UTC
  • mto: This revision was merged to the branch mainline in revision 794.
  • Revision ID: jelmer@samba.org-20120709152326-dzxb8zoz0btull7n
Remove bzr-notify.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
"""
6
6
 
7
7
__copyright__ = "Copyright 2005 Canonical Ltd."
8
 
__author__    = "Scott James Remnant <scott@ubuntu.com>"
 
8
__author__ = "Scott James Remnant <scott@ubuntu.com>"
9
9
 
10
10
 
11
11
from cStringIO import StringIO
12
12
 
13
 
import pygtk
14
 
pygtk.require("2.0")
15
 
import gtk
16
 
import pango
17
 
import os
18
 
import re
19
13
import sys
20
14
import inspect
21
 
try:
22
 
    from xml.etree.ElementTree import Element, SubElement, tostring
23
 
except ImportError:
24
 
    from elementtree.ElementTree import Element, SubElement, tostring
25
15
 
 
16
from gi.repository import Gtk
 
17
from gi.repository import Pango
26
18
try:
27
 
    import gtksourceview2
 
19
    from gi.repository import GtkSource
28
20
    have_gtksourceview = True
29
21
except ImportError:
30
22
    have_gtksourceview = False
31
 
try:
32
 
    import gconf
33
 
    have_gconf = True
34
 
except ImportError:
35
 
    have_gconf = False
36
23
 
37
24
from bzrlib import (
38
25
    errors,
40
27
    osutils,
41
28
    urlutils,
42
29
    workingtree,
43
 
)
44
 
from bzrlib.diff import show_diff_trees, internal_diff
 
30
    )
 
31
from bzrlib.diff import show_diff_trees
45
32
from bzrlib.patches import parse_patches
46
 
from bzrlib.trace import warning
47
 
from bzrlib.plugins.gtk import _i18n
 
33
from bzrlib.plugins.gtk.dialog import (
 
34
    error_dialog,
 
35
    info_dialog,
 
36
    warning_dialog,
 
37
    )
 
38
from bzrlib.plugins.gtk.i18n import _i18n
48
39
from bzrlib.plugins.gtk.window import Window
49
 
from dialog import error_dialog, info_dialog, warning_dialog
50
40
 
51
41
 
52
42
def fallback_guess_language(slm, content_type):
62
52
    pass
63
53
 
64
54
 
65
 
class DiffFileView(gtk.ScrolledWindow):
 
55
class DiffFileView(Gtk.ScrolledWindow):
66
56
    """Window for displaying diffs from a diff file"""
67
57
 
 
58
    SHOW_WIDGETS = True
 
59
 
68
60
    def __init__(self):
69
 
        gtk.ScrolledWindow.__init__(self)
 
61
        super(DiffFileView, self).__init__()
70
62
        self.construct()
71
63
        self._diffs = {}
72
64
 
73
65
    def construct(self):
74
 
        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
75
 
        self.set_shadow_type(gtk.SHADOW_IN)
 
66
        self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
 
67
        self.set_shadow_type(Gtk.ShadowType.IN)
76
68
 
77
69
        if have_gtksourceview:
78
 
            self.buffer = gtksourceview2.Buffer()
79
 
            slm = gtksourceview2.LanguageManager()
80
 
            guess_language = getattr(gtksourceview2.LanguageManager, 
81
 
                "guess_language", fallback_guess_language)
82
 
            gsl = guess_language(slm, content_type="text/x-patch")
83
 
            if have_gconf:
84
 
                self.apply_gedit_colors(self.buffer)
85
 
            self.apply_colordiff_colors(self.buffer)
86
 
            self.buffer.set_language(gsl)
 
70
            self.buffer = GtkSource.Buffer()
 
71
            lang_manager = GtkSource.LanguageManager.get_default()
 
72
            language = lang_manager.guess_language(None, "text/x-patch")
 
73
            self.buffer.set_language(language)
87
74
            self.buffer.set_highlight_syntax(True)
88
 
 
89
 
            self.sourceview = gtksourceview2.View(self.buffer)
 
75
            self.sourceview = GtkSource.View(buffer=self.buffer)
90
76
        else:
91
 
            self.buffer = gtk.TextBuffer()
92
 
            self.sourceview = gtk.TextView(self.buffer)
 
77
            self.buffer = Gtk.TextBuffer()
 
78
            self.sourceview = Gtk.TextView(self.buffer)
93
79
 
94
80
        self.sourceview.set_editable(False)
95
 
        self.sourceview.modify_font(pango.FontDescription("Monospace"))
 
81
        self.sourceview.override_font(Pango.FontDescription("Monospace"))
96
82
        self.add(self.sourceview)
97
 
        self.sourceview.show()
98
 
 
99
 
    @staticmethod
100
 
    def apply_gedit_colors(buf):
101
 
        """Set style to that specified in gedit configuration.
102
 
 
103
 
        This method needs the gconf module.
104
 
 
105
 
        :param buf: a gtksourceview2.Buffer object.
106
 
        """
107
 
        GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
108
 
        GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
109
 
 
110
 
        client = gconf.client_get_default()
111
 
        style_scheme_name = client.get_string(GEDIT_SCHEME_PATH)
112
 
        if style_scheme_name is not None:
113
 
            style_scheme_mgr = gtksourceview2.StyleSchemeManager()
114
 
            style_scheme_mgr.append_search_path(GEDIT_USER_STYLES_PATH)
115
 
            
116
 
            style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
117
 
            
118
 
            if style_scheme is not None:
119
 
                buf.set_style_scheme(style_scheme)
120
 
 
121
 
    @classmethod
122
 
    def apply_colordiff_colors(klass, buf):
123
 
        """Set style colors for lang using the colordiff configuration file.
124
 
 
125
 
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
126
 
 
127
 
        :param buf: a "Diff" gtksourceview2.Buffer object.
128
 
        """
129
 
        scheme_manager = gtksourceview2.StyleSchemeManager()
130
 
        style_scheme = scheme_manager.get_scheme('colordiff')
131
 
        
132
 
        # if style scheme not found, we'll generate it from colordiffrc
133
 
        # TODO: reload if colordiffrc has changed.
134
 
        if style_scheme is None:
135
 
            colors = {}
136
 
 
137
 
            for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
138
 
                f = os.path.expanduser(f)
139
 
                if os.path.exists(f):
140
 
                    try:
141
 
                        f = file(f)
142
 
                    except IOError, e:
143
 
                        warning('could not open file %s: %s' % (f, str(e)))
144
 
                    else:
145
 
                        colors.update(klass.parse_colordiffrc(f))
146
 
                        f.close()
147
 
 
148
 
            if not colors:
149
 
                # ~/.colordiffrc does not exist
150
 
                return
151
 
            
152
 
            mapping = {
153
 
                # map GtkSourceView2 scheme styles to colordiff names
154
 
                # since GSV is richer, accept new names for extra bits,
155
 
                # defaulting to old names if they're not present
156
 
                'diff:added-line': ['newtext'],
157
 
                'diff:removed-line': ['oldtext'],
158
 
                'diff:location': ['location', 'diffstuff'],
159
 
                'diff:file': ['file', 'diffstuff'],
160
 
                'diff:special-case': ['specialcase', 'diffstuff'],
161
 
            }
162
 
            
163
 
            converted_colors = {}
164
 
            for name, values in mapping.items():
165
 
                color = None
166
 
                for value in values:
167
 
                    color = colors.get(value, None)
168
 
                    if color is not None:
169
 
                        break
170
 
                if color is None:
171
 
                    continue
172
 
                converted_colors[name] = color
173
 
            
174
 
            # some xml magic to produce needed style scheme description
175
 
            e_style_scheme = Element('style-scheme')
176
 
            e_style_scheme.set('id', 'colordiff')
177
 
            e_style_scheme.set('_name', 'ColorDiff')
178
 
            e_style_scheme.set('version', '1.0')
179
 
            for name, color in converted_colors.items():
180
 
                style = SubElement(e_style_scheme, 'style')
181
 
                style.set('name', name)
182
 
                style.set('foreground', '#%s' % color)
183
 
            
184
 
            scheme_xml = tostring(e_style_scheme, 'UTF-8')
185
 
            if not os.path.exists(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles')):
186
 
                os.makedirs(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles'))
187
 
            file(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles/colordiff.xml'), 'w').write(scheme_xml)
188
 
            
189
 
            scheme_manager.force_rescan()
190
 
            style_scheme = scheme_manager.get_scheme('colordiff')
191
 
        
192
 
        buf.set_style_scheme(style_scheme)
193
 
 
194
 
    @staticmethod
195
 
    def parse_colordiffrc(fileobj):
196
 
        """Parse fileobj as a colordiff configuration file.
197
 
 
198
 
        :return: A dict with the key -> value pairs.
199
 
        """
200
 
        colors = {}
201
 
        for line in fileobj:
202
 
            if re.match(r'^\s*#', line):
203
 
                continue
204
 
            if '=' not in line:
205
 
                continue
206
 
            key, val = line.split('=', 1)
207
 
            colors[key.strip()] = val.strip()
208
 
        return colors
 
83
        if self.SHOW_WIDGETS:
 
84
            self.sourceview.show()
209
85
 
210
86
    def set_trees(self, rev_tree, parent_tree):
211
87
        self.rev_tree = rev_tree
216
92
#        self.parent_tree.lock_read()
217
93
#        self.rev_tree.lock_read()
218
94
#        try:
219
 
#            self.delta = iter_changes_to_status(self.parent_tree, self.rev_tree)
 
95
#            self.delta = iter_changes_to_status(
 
96
#               self.parent_tree, self.rev_tree)
220
97
#            self.path_to_status = {}
221
98
#            self.path_to_diff = {}
222
99
#            source_inv = self.parent_tree.inventory
223
100
#            target_inv = self.rev_tree.inventory
224
101
#            for (file_id, real_path, change_type, display_path) in self.delta:
225
 
#                self.path_to_status[real_path] = u'=== %s %s' % (change_type, display_path)
 
102
#                self.path_to_status[real_path] = u'=== %s %s' % (
 
103
#                    change_type, display_path)
226
104
#                if change_type in ('modified', 'renamed and modified'):
227
105
#                    source_ie = source_inv[file_id]
228
106
#                    target_ie = target_inv[file_id]
230
108
#                    source_ie.diff(internal_diff, *old path, *old_tree,
231
109
#                                   *new_path, target_ie, self.rev_tree,
232
110
#                                   sio)
233
 
#                    self.path_to_diff[real_path] = 
 
111
#                    self.path_to_diff[real_path] =
234
112
#
235
113
#        finally:
236
114
#            self.rev_tree.unlock()
250
128
    """This is the soft and chewy filling for a DiffWindow."""
251
129
 
252
130
    def __init__(self):
253
 
        DiffFileView.__init__(self)
 
131
        super(DiffView, self).__init__()
254
132
        self.rev_tree = None
255
133
        self.parent_tree = None
256
134
 
280
158
        self.buffer.set_text(decoded.encode('UTF-8'))
281
159
 
282
160
 
283
 
class DiffWidget(gtk.HPaned):
 
161
class DiffWidget(Gtk.HPaned):
284
162
    """Diff widget
285
163
 
286
164
    """
 
165
 
 
166
    SHOW_WIDGETS = True
 
167
 
287
168
    def __init__(self):
288
169
        super(DiffWidget, self).__init__()
289
170
 
290
171
        # The file hierarchy: a scrollable treeview
291
 
        scrollwin = gtk.ScrolledWindow()
292
 
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
293
 
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
 
172
        scrollwin = Gtk.ScrolledWindow()
 
173
        scrollwin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
 
174
        scrollwin.set_shadow_type(Gtk.ShadowType.IN)
294
175
        self.pack1(scrollwin)
295
 
        scrollwin.show()
296
 
        
297
 
        self.model = gtk.TreeStore(str, str)
298
 
        self.treeview = gtk.TreeView(self.model)
 
176
        if self.SHOW_WIDGETS:
 
177
            scrollwin.show()
 
178
 
 
179
        self.model = Gtk.TreeStore(str, str)
 
180
        self.treeview = Gtk.TreeView(model=self.model)
299
181
        self.treeview.set_headers_visible(False)
300
182
        self.treeview.set_search_column(1)
301
183
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
302
184
        scrollwin.add(self.treeview)
303
 
        self.treeview.show()
 
185
        if self.SHOW_WIDGETS:
 
186
            self.treeview.show()
304
187
 
305
 
        cell = gtk.CellRendererText()
 
188
        cell = Gtk.CellRendererText()
306
189
        cell.set_property("width-chars", 20)
307
 
        column = gtk.TreeViewColumn()
308
 
        column.pack_start(cell, expand=True)
 
190
        column = Gtk.TreeViewColumn()
 
191
        column.pack_start(cell, True)
309
192
        column.add_attribute(cell, "text", 0)
310
193
        self.treeview.append_column(column)
311
194
 
321
204
        if getattr(self, 'diff_view', None) is None:
322
205
            self.diff_view = DiffFileView()
323
206
            self.pack2(self.diff_view)
324
 
        self.diff_view.show()
 
207
        if self.SHOW_WIDGETS:
 
208
            self.diff_view.show()
325
209
        for oldname, newname, patch in sections:
326
210
            self.diff_view._diffs[newname] = str(patch)
327
211
            if newname is None:
338
222
        if getattr(self, 'diff_view', None) is None:
339
223
            self.diff_view = DiffView()
340
224
            self.pack2(self.diff_view)
341
 
        self.diff_view.show()
 
225
        if self.SHOW_WIDGETS:
 
226
            self.diff_view.show()
342
227
        self.diff_view.set_trees(rev_tree, parent_tree)
343
228
        self.rev_tree = rev_tree
344
229
        self.parent_tree = parent_tree
346
231
        self.model.clear()
347
232
        delta = self.rev_tree.changes_from(self.parent_tree)
348
233
 
349
 
        self.model.append(None, [ "Complete Diff", "" ])
 
234
        self.model.append(None, ["Complete Diff", ""])
350
235
 
351
236
        if len(delta.added):
352
 
            titer = self.model.append(None, [ "Added", None ])
 
237
            titer = self.model.append(None, ["Added", None])
353
238
            for path, id, kind in delta.added:
354
 
                self.model.append(titer, [ path, path ])
 
239
                self.model.append(titer, [path, path])
355
240
 
356
241
        if len(delta.removed):
357
 
            titer = self.model.append(None, [ "Removed", None ])
 
242
            titer = self.model.append(None, ["Removed", None])
358
243
            for path, id, kind in delta.removed:
359
 
                self.model.append(titer, [ path, path ])
 
244
                self.model.append(titer, [path, path])
360
245
 
361
246
        if len(delta.renamed):
362
 
            titer = self.model.append(None, [ "Renamed", None ])
 
247
            titer = self.model.append(None, ["Renamed", None])
363
248
            for oldpath, newpath, id, kind, text_modified, meta_modified \
364
249
                    in delta.renamed:
365
 
                self.model.append(titer, [ oldpath, newpath ])
 
250
                self.model.append(titer, [oldpath, newpath])
366
251
 
367
252
        if len(delta.modified):
368
 
            titer = self.model.append(None, [ "Modified", None ])
 
253
            titer = self.model.append(None, ["Modified", None])
369
254
            for path, id, kind, text_modified, meta_modified in delta.modified:
370
 
                self.model.append(titer, [ path, path ])
 
255
                self.model.append(titer, [path, path])
371
256
 
372
257
        self.treeview.expand_all()
373
258
        self.diff_view.show_diff(None)
382
267
                    break
383
268
        if tv_path is None:
384
269
            raise errors.NoSuchFile(file_path)
385
 
        self.treeview.set_cursor(tv_path)
 
270
        self.treeview.set_cursor(tv_path, None, False)
386
271
        self.treeview.scroll_to_cell(tv_path)
387
272
 
388
273
    def _treeview_cursor_cb(self, *args):
389
274
        """Callback for when the treeview cursor changes."""
390
275
        (path, col) = self.treeview.get_cursor()
391
 
        specific_files = [ self.model[path][1] ]
392
 
        if specific_files == [ None ]:
393
 
            return
394
 
        elif specific_files == [ "" ]:
 
276
        if path is None:
 
277
            return
 
278
        specific_files = [self.model[path][1]]
 
279
        if specific_files == [None]:
 
280
            return
 
281
        elif specific_files == [""]:
395
282
            specific_files = None
396
 
        
 
283
 
397
284
        self.diff_view.show_diff(specific_files)
398
 
    
 
285
 
399
286
    def _on_wraplines_toggled(self, widget=None, wrap=False):
400
287
        """Callback for when the wrap lines checkbutton is toggled"""
401
288
        if wrap or widget.get_active():
402
 
            self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_WORD)
 
289
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.WORD)
403
290
        else:
404
 
            self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_NONE)
 
291
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.NONE)
 
292
 
405
293
 
406
294
class DiffWindow(Window):
407
295
    """Diff window.
410
298
    differences between two revisions on a branch.
411
299
    """
412
300
 
 
301
    SHOW_WIDGETS = True
 
302
 
413
303
    def __init__(self, parent=None, operations=None):
414
 
        Window.__init__(self, parent)
 
304
        super(DiffWindow, self).__init__(parent=parent)
415
305
        self.set_border_width(0)
416
 
        self.set_title("bzrk diff")
 
306
        self.set_title("bzr diff")
417
307
 
418
308
        # Use two thirds of the screen by default
419
309
        screen = self.get_screen()
425
315
 
426
316
    def construct(self, operations):
427
317
        """Construct the window contents."""
428
 
        self.vbox = gtk.VBox()
 
318
        self.vbox = Gtk.VBox()
429
319
        self.add(self.vbox)
430
 
        self.vbox.show()
 
320
        if self.SHOW_WIDGETS:
 
321
            self.vbox.show()
431
322
        self.diff = DiffWidget()
432
323
        self.vbox.pack_end(self.diff, True, True, 0)
433
 
        self.diff.show_all()
 
324
        if self.SHOW_WIDGETS:
 
325
            self.diff.show_all()
434
326
        # Build after DiffWidget to connect signals
435
327
        menubar = self._get_menu_bar()
436
328
        self.vbox.pack_start(menubar, False, False, 0)
437
329
        hbox = self._get_button_bar(operations)
438
330
        if hbox is not None:
439
331
            self.vbox.pack_start(hbox, False, True, 0)
440
 
        
441
 
    
 
332
 
442
333
    def _get_menu_bar(self):
443
 
        menubar = gtk.MenuBar()
 
334
        menubar = Gtk.MenuBar()
444
335
        # View menu
445
 
        mb_view = gtk.MenuItem(_i18n("_View"))
446
 
        mb_view_menu = gtk.Menu()
447
 
        mb_view_wrapsource = gtk.CheckMenuItem(_i18n("Wrap _Long Lines"))
 
336
        mb_view = Gtk.MenuItem.new_with_mnemonic(_i18n("_View"))
 
337
        mb_view_menu = Gtk.Menu()
 
338
        mb_view_wrapsource = Gtk.CheckMenuItem.new_with_mnemonic(
 
339
            _i18n("Wrap _Long Lines"))
448
340
        mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
449
 
        mb_view_wrapsource.show()
450
341
        mb_view_menu.append(mb_view_wrapsource)
451
 
        mb_view.show()
452
342
        mb_view.set_submenu(mb_view_menu)
453
 
        mb_view.show()
454
343
        menubar.append(mb_view)
455
 
        menubar.show()
 
344
        if self.SHOW_WIDGETS:
 
345
            menubar.show_all()
456
346
        return menubar
457
 
    
 
347
 
458
348
    def _get_button_bar(self, operations):
459
349
        """Return a button bar to use.
460
350
 
462
352
        """
463
353
        if operations is None:
464
354
            return None
465
 
        hbox = gtk.HButtonBox()
466
 
        hbox.set_layout(gtk.BUTTONBOX_START)
 
355
        hbox = Gtk.HButtonBox()
 
356
        hbox.set_layout(Gtk.ButtonBoxStyle.START)
467
357
        for title, method in operations:
468
 
            merge_button = gtk.Button(title)
469
 
            merge_button.show()
470
 
            merge_button.set_relief(gtk.RELIEF_NONE)
 
358
            merge_button = Gtk.Button(title)
 
359
            if self.SHOW_WIDGETS:
 
360
                merge_button.show()
 
361
            merge_button.set_relief(Gtk.ReliefStyle.NONE)
471
362
            merge_button.connect("clicked", method)
472
 
            hbox.pack_start(merge_button, expand=False, fill=True)
473
 
        hbox.show()
 
363
            hbox.pack_start(merge_button, False, True, 0)
 
364
        if self.SHOW_WIDGETS:
 
365
            hbox.show()
474
366
        return hbox
475
367
 
476
368
    def _get_merge_target(self):
477
 
        d = gtk.FileChooserDialog('Merge branch', self,
478
 
                                  gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
479
 
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
480
 
                                           gtk.STOCK_CANCEL,
481
 
                                           gtk.RESPONSE_CANCEL,))
 
369
        d = Gtk.FileChooserDialog('Merge branch', self,
 
370
                                  Gtk.FileChooserAction.SELECT_FOLDER,
 
371
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
 
372
                                           Gtk.STOCK_CANCEL,
 
373
                                           Gtk.ResponseType.CANCEL,))
482
374
        try:
483
375
            result = d.run()
484
 
            if result != gtk.RESPONSE_OK:
 
376
            if result != Gtk.ResponseType.OK:
485
377
                raise SelectCancelled()
486
378
            return d.get_current_folder_uri()
487
379
        finally:
501
393
        error_dialog('Error', str(e))
502
394
 
503
395
    def _get_save_path(self, basename):
504
 
        d = gtk.FileChooserDialog('Save As', self,
505
 
                                  gtk.FILE_CHOOSER_ACTION_SAVE,
506
 
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
507
 
                                           gtk.STOCK_CANCEL,
508
 
                                           gtk.RESPONSE_CANCEL,))
 
396
        d = Gtk.FileChooserDialog('Save As', self,
 
397
                                  Gtk.FileChooserAction.SAVE,
 
398
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
 
399
                                           Gtk.STOCK_CANCEL,
 
400
                                           Gtk.ResponseType.CANCEL,))
509
401
        d.set_current_name(basename)
510
402
        try:
511
403
            result = d.run()
512
 
            if result != gtk.RESPONSE_OK:
 
404
            if result != Gtk.ResponseType.OK:
513
405
                raise SelectCancelled()
514
406
            return urlutils.local_path_from_url(d.get_uri())
515
407
        finally:
577
469
class MergeDirectiveController(DiffController):
578
470
 
579
471
    def __init__(self, path, directive, window=None):
580
 
        DiffController.__init__(self, path, directive.patch.splitlines(True),
581
 
                                window)
 
472
        super(MergeDirectiveController, self).__init__(
 
473
            path, directive.patch.splitlines(True), window)
582
474
        self.directive = directive
583
475
        self.merge_target = None
584
476