dev: format code using "garden fmt" (black)
[git-cola.git] / cola / difftool.py
blob86cf7ade896efcd5967010667156a6ed0e8afc3c
1 from __future__ import absolute_import, division, print_function, unicode_literals
3 from qtpy import QtWidgets
4 from qtpy.QtCore import Qt
6 from . import cmds
7 from . import gitcmds
8 from . import hotkeys
9 from . import icons
10 from . import qtutils
11 from . import utils
12 from .i18n import N_
13 from .widgets import completion
14 from .widgets import defs
15 from .widgets import filetree
16 from .widgets import standard
19 def diff_commits(context, parent, a, b):
20 """Show a dialog for diffing two commits"""
21 dlg = Difftool(context, parent, a=a, b=b)
22 dlg.show()
23 dlg.raise_()
24 return dlg.exec_() == QtWidgets.QDialog.Accepted
27 def diff_expression(
28 context, parent, expr, create_widget=False, hide_expr=False, focus_tree=False
30 """Show a diff dialog for diff expressions"""
31 dlg = Difftool(
32 context, parent, expr=expr, hide_expr=hide_expr, focus_tree=focus_tree
34 if create_widget:
35 return dlg
36 dlg.show()
37 dlg.raise_()
38 return dlg.exec_() == QtWidgets.QDialog.Accepted
41 class Difftool(standard.Dialog):
42 def __init__(
43 self,
44 context,
45 parent,
46 a=None,
47 b=None,
48 expr=None,
49 title=None,
50 hide_expr=False,
51 focus_tree=False,
53 """Show files with differences and launch difftool"""
55 standard.Dialog.__init__(self, parent=parent)
57 self.context = context
58 self.a = a
59 self.b = b
60 self.diff_expr = expr
62 if title is None:
63 title = N_('git-cola diff')
65 self.setWindowTitle(title)
66 self.setWindowModality(Qt.WindowModal)
68 self.expr = completion.GitRefLineEdit(context, parent=self)
69 if expr is not None:
70 self.expr.setText(expr)
72 if expr is None or hide_expr:
73 self.expr.hide()
75 self.tree = filetree.FileTree(parent=self)
77 self.diff_button = qtutils.create_button(
78 text=N_('Compare'), icon=icons.diff(), enabled=False, default=True
80 self.diff_button.setShortcut(hotkeys.DIFF)
82 self.diff_all_button = qtutils.create_button(
83 text=N_('Compare All'), icon=icons.diff()
85 self.edit_button = qtutils.edit_button()
86 self.edit_button.setShortcut(hotkeys.EDIT)
88 self.close_button = qtutils.close_button()
90 self.button_layout = qtutils.hbox(
91 defs.no_margin,
92 defs.spacing,
93 qtutils.STRETCH,
94 self.close_button,
95 self.edit_button,
96 self.diff_all_button,
97 self.diff_button,
100 self.main_layout = qtutils.vbox(
101 defs.margin, defs.spacing, self.expr, self.tree, self.button_layout
103 self.setLayout(self.main_layout)
105 # pylint: disable=no-member
106 self.tree.itemSelectionChanged.connect(self.tree_selection_changed)
107 self.tree.itemDoubleClicked.connect(self.tree_double_clicked)
108 self.tree.up.connect(self.focus_input)
110 self.expr.textChanged.connect(self.text_changed)
112 self.expr.activated.connect(self.focus_tree)
113 self.expr.down.connect(self.focus_tree)
114 self.expr.enter.connect(self.focus_tree)
116 qtutils.connect_button(self.diff_button, self.diff)
117 qtutils.connect_button(self.diff_all_button, lambda: self.diff(dir_diff=True))
118 qtutils.connect_button(self.edit_button, self.edit)
119 qtutils.connect_button(self.close_button, self.close)
121 qtutils.add_action(self, 'Focus Input', self.focus_input, hotkeys.FOCUS)
122 qtutils.add_action(
123 self,
124 'Diff All',
125 lambda: self.diff(dir_diff=True),
126 hotkeys.CTRL_ENTER,
127 hotkeys.CTRL_RETURN,
129 qtutils.add_close_action(self)
131 self.init_state(None, self.resize_widget, parent)
133 self.refresh()
134 if focus_tree:
135 self.focus_tree()
137 def resize_widget(self, parent):
138 """Set the initial size of the widget"""
139 width, height = qtutils.default_size(parent, 720, 420)
140 self.resize(width, height)
142 def focus_tree(self):
143 """Focus the files tree"""
144 self.tree.setFocus()
146 def focus_input(self):
147 """Focus the expression input"""
148 self.expr.setFocus()
150 def text_changed(self, txt):
151 self.diff_expr = txt
152 self.refresh()
154 def refresh(self):
155 """Redo the diff when the expression changes"""
156 if self.diff_expr is not None:
157 self.diff_arg = utils.shell_split(self.diff_expr)
158 elif self.b is None:
159 self.diff_arg = [self.a]
160 else:
161 self.diff_arg = [self.a, self.b]
162 self.refresh_filenames()
164 def refresh_filenames(self):
165 context = self.context
166 if self.a and self.b is None:
167 filenames = gitcmds.diff_index_filenames(context, self.a)
168 else:
169 filenames = gitcmds.diff(context, self.diff_arg)
170 self.tree.set_filenames(filenames, select=True)
172 def tree_selection_changed(self):
173 has_selection = self.tree.has_selection()
174 self.diff_button.setEnabled(has_selection)
175 self.diff_all_button.setEnabled(has_selection)
177 def tree_double_clicked(self, item, _column):
178 path = filetree.filename_from_item(item)
179 left, right = self._left_right_args()
180 cmds.difftool_launch(self.context, left=left, right=right, paths=[path])
182 def diff(self, dir_diff=False):
183 paths = self.tree.selected_filenames()
184 left, right = self._left_right_args()
185 cmds.difftool_launch(
186 self.context, left=left, right=right, paths=paths, dir_diff=dir_diff
189 def _left_right_args(self):
190 if self.diff_arg:
191 left = self.diff_arg[0]
192 else:
193 left = None
194 if len(self.diff_arg) > 1:
195 right = self.diff_arg[1]
196 else:
197 right = None
198 return (left, right)
200 def edit(self):
201 paths = self.tree.selected_filenames()
202 cmds.do(cmds.Edit, self.context, paths)