Fix all flake8 errors

This commit is contained in:
Romain Failliot 2021-11-22 20:11:51 -05:00
parent 362c59f150
commit 8d25396f68
18 changed files with 487 additions and 420 deletions

6
.flake8 Normal file
View File

@ -0,0 +1,6 @@
[flake8]
builtins = _
max-line-length = 100
# Temporary
exclude = src/diffuse/main.py

View File

@ -19,17 +19,14 @@
import os import os
# pylint: disable=wrong-import-position from diffuse import constants
from diffuse import utils
import gi import gi
gi.require_version('GObject', '2.0') gi.require_version('GObject', '2.0')
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import GObject, Gtk from gi.repository import GObject, Gtk # noqa: E402
# pylint: enable=wrong-import-position
# pylint: disable-next=no-name-in-module
from diffuse import constants
from diffuse import utils
# the about dialog # the about dialog
class AboutDialog(Gtk.AboutDialog): class AboutDialog(Gtk.AboutDialog):
@ -62,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.''')] 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.''')]
self.set_license(''.join(license_text)) self.set_license(''.join(license_text))
# custom dialogue for picking files with widgets for specifying the encoding # custom dialogue for picking files with widgets for specifying the encoding
# and revision # and revision
class FileChooserDialog(Gtk.FileChooserDialog): class FileChooserDialog(Gtk.FileChooserDialog):
@ -96,7 +94,7 @@ class FileChooserDialog(Gtk.FileChooserDialog):
hbox.pack_end(label, False, False, 0) hbox.pack_end(label, False, False, 0)
label.show() label.show()
self.vbox.pack_start(hbox, False, False, 0) # pylint: disable=no-member self.vbox.pack_start(hbox, False, False, 0)
hbox.show() hbox.show()
self.set_current_folder(self.last_chosen_folder) self.set_current_folder(self.last_chosen_folder)
self.connect('current-folder-changed', self._current_folder_changed_cb) self.connect('current-folder-changed', self._current_folder_changed_cb)
@ -110,10 +108,10 @@ class FileChooserDialog(Gtk.FileChooserDialog):
def get_revision(self): def get_revision(self):
return self.revision.get_text() return self.revision.get_text()
# pylint: disable-next=arguments-differ
def get_filename(self): def get_filename(self):
# convert from UTF-8 string to unicode # convert from UTF-8 string to unicode
return Gtk.FileChooserDialog.get_filename(self) # pylint: disable=no-member return Gtk.FileChooserDialog.get_filename(self)
# dialogue used to search for text # dialogue used to search for text
class NumericDialog(Gtk.Dialog): class NumericDialog(Gtk.Dialog):
@ -138,12 +136,13 @@ class NumericDialog(Gtk.Dialog):
vbox.pack_start(hbox, True, True, 0) vbox.pack_start(hbox, True, True, 0)
hbox.show() hbox.show()
self.vbox.pack_start(vbox, False, False, 0) # pylint: disable=no-member self.vbox.pack_start(vbox, False, False, 0)
vbox.show() vbox.show()
def button_cb(self, widget): def button_cb(self, widget):
self.response(Gtk.ResponseType.ACCEPT) self.response(Gtk.ResponseType.ACCEPT)
# dialogue used to search for text # dialogue used to search for text
class SearchDialog(Gtk.Dialog): class SearchDialog(Gtk.Dialog):
def __init__(self, parent, pattern=None, history=None): def __init__(self, parent, pattern=None, history=None):
@ -190,7 +189,7 @@ class SearchDialog(Gtk.Dialog):
vbox.pack_start(button, False, False, 0) vbox.pack_start(button, False, False, 0)
button.show() button.show()
self.vbox.pack_start(vbox, False, False, 0) # pylint: disable=no-member self.vbox.pack_start(vbox, False, False, 0)
vbox.show() vbox.show()
# callback used when the Enter key is pressed # callback used when the Enter key is pressed

View File

@ -25,22 +25,9 @@ import shlex
import stat import stat
import webbrowser import webbrowser
# pylint: disable=wrong-import-position
import gi
gi.require_version('GObject', '2.0')
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('GdkPixbuf', '2.0')
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import GObject, Gtk, Gdk, GdkPixbuf, Pango, PangoCairo
# pylint: enable=wrong-import-position
from urllib.parse import urlparse from urllib.parse import urlparse
# pylint: disable-next=no-name-in-module
from diffuse import constants from diffuse import constants
from diffuse import utils from diffuse import utils
from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog
from diffuse.preferences import Preferences from diffuse.preferences import Preferences
@ -49,6 +36,16 @@ from diffuse.vcs.vcs_registry import VcsRegistry
from diffuse.widgets import FileDiffViewer from diffuse.widgets import FileDiffViewer
from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE
import gi
gi.require_version('GObject', '2.0')
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('GdkPixbuf', '2.0')
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import GObject, Gtk, Gdk, GdkPixbuf, Pango, PangoCairo # noqa: E402
theVCSs = VcsRegistry() theVCSs = VcsRegistry()
# widget classed to create notebook tabs with labels and a close button # widget classed to create notebook tabs with labels and a close button
@ -986,7 +983,7 @@ class Diffuse(Gtk.Window):
treeview.connect('row-activated', self._confirmClose_row_activated_cb, model) treeview.connect('row-activated', self._confirmClose_row_activated_cb, model)
sw.add(treeview) sw.add(treeview)
treeview.show() treeview.show()
dialog.vbox.pack_start(sw, True, True, 0) # pylint: disable=no-member dialog.vbox.pack_start(sw, True, True, 0)
sw.show() sw.show()
# add custom set of action buttons # add custom set of action buttons
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)

View File

@ -23,16 +23,13 @@ import os
import shlex import shlex
import sys import sys
# pylint: disable=wrong-import-position from diffuse import constants
from diffuse import utils
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk # noqa: E402
# pylint: enable=wrong-import-position
# pylint: disable-next=no-name-in-module
from diffuse import constants
from diffuse import utils
# class to store preferences and construct a dialogue for manipulating them # class to store preferences and construct a dialogue for manipulating them
class Preferences: class Preferences:
@ -75,11 +72,11 @@ class Preferences:
# [ 'String', name, default, label ] # [ 'String', name, default, label ]
# [ 'File', name, default, label ] # [ 'File', name, default, label ]
# [ 'Font', name, default, label ] # [ 'Font', name, default, label ]
# pylint: disable=line-too-long
self.template = [ self.template = [
'FolderSet', 'FolderSet',
_('Display'), _('Display'),
[ 'List', [
'List',
['Font', 'display_font', 'Monospace 10', _('Font')], ['Font', 'display_font', 'Monospace 10', _('Font')],
['Integer', 'display_tab_width', 8, _('Tab width'), 1, 1024], ['Integer', 'display_tab_width', 8, _('Tab width'), 1, 1024],
['Boolean', 'display_show_right_margin', True, _('Show right margin')], ['Boolean', 'display_show_right_margin', True, _('Show right margin')],
@ -87,38 +84,41 @@ class Preferences:
['Boolean', 'display_show_line_numbers', True, _('Show line numbers')], ['Boolean', 'display_show_line_numbers', True, _('Show line numbers')],
['Boolean', 'display_show_whitespace', False, _('Show white space characters')], ['Boolean', 'display_show_whitespace', False, _('Show white space characters')],
['Boolean', 'display_ignore_case', False, _('Ignore case differences')], ['Boolean', 'display_ignore_case', False, _('Ignore case differences')],
[ 'Boolean', 'display_ignore_whitespace', False, _('Ignore white space differences') ], ['Boolean', 'display_ignore_whitespace', False, _('Ignore white space differences')], # noqa: E501
[ 'Boolean', 'display_ignore_whitespace_changes', False, _('Ignore changes to white space') ], ['Boolean', 'display_ignore_whitespace_changes', False, _('Ignore changes to white space')], # noqa: E501
['Boolean', 'display_ignore_blanklines', False, _('Ignore blank line differences')], ['Boolean', 'display_ignore_blanklines', False, _('Ignore blank line differences')],
['Boolean', 'display_ignore_endofline', False, _('Ignore end of line differences')] ['Boolean', 'display_ignore_endofline', False, _('Ignore end of line differences')]
], ],
_('Alignment'), _('Alignment'),
[ 'List', [
'List',
['Boolean', 'align_ignore_case', False, _('Ignore case')], ['Boolean', 'align_ignore_case', False, _('Ignore case')],
['Boolean', 'align_ignore_whitespace', True, _('Ignore white space')], ['Boolean', 'align_ignore_whitespace', True, _('Ignore white space')],
[ 'Boolean', 'align_ignore_whitespace_changes', False, _('Ignore changes to white space') ], ['Boolean', 'align_ignore_whitespace_changes', False, _('Ignore changes to white space')], # noqa: E501
['Boolean', 'align_ignore_blanklines', False, _('Ignore blank lines')], ['Boolean', 'align_ignore_blanklines', False, _('Ignore blank lines')],
['Boolean', 'align_ignore_endofline', True, _('Ignore end of line characters')] ['Boolean', 'align_ignore_endofline', True, _('Ignore end of line characters')]
], ],
_('Editor'), _('Editor'),
[ 'List', [
'List',
['Boolean', 'editor_auto_indent', True, _('Auto indent')], ['Boolean', 'editor_auto_indent', True, _('Auto indent')],
['Boolean', 'editor_expand_tabs', False, _('Expand tabs to spaces')], ['Boolean', 'editor_expand_tabs', False, _('Expand tabs to spaces')],
['Integer', 'editor_soft_tab_width', 8, _('Soft tab width'), 1, 1024] ['Integer', 'editor_soft_tab_width', 8, _('Soft tab width'), 1, 1024]
], ],
_('Tabs'), _('Tabs'),
[ 'List', [
'List',
['Integer', 'tabs_default_panes', 2, _('Default panes'), 2, 16], ['Integer', 'tabs_default_panes', 2, _('Default panes'), 2, 16],
['Boolean', 'tabs_always_show', False, _('Always show the tab bar')], ['Boolean', 'tabs_always_show', False, _('Always show the tab bar')],
[ 'Boolean', 'tabs_warn_before_quit', True, _('Warn me when closing a tab will quit %s') % constants.APP_NAME ] ['Boolean', 'tabs_warn_before_quit', True, _('Warn me when closing a tab will quit %s') % constants.APP_NAME] # noqa: E501
], ],
_('Regional Settings'), _('Regional Settings'),
[ 'List', [
[ 'Encoding', 'encoding_default_codec', sys.getfilesystemencoding(), _('Default codec') ], 'List',
[ 'String', 'encoding_auto_detect_codecs', ' '.join(auto_detect_codecs), _('Order of codecs used to identify encoding') ] ['Encoding', 'encoding_default_codec', sys.getfilesystemencoding(), _('Default codec')], # noqa: E501
['String', 'encoding_auto_detect_codecs', ' '.join(auto_detect_codecs), _('Order of codecs used to identify encoding')] # noqa: E501
], ],
] ]
# pylint: disable=line-too-long
# conditions used to determine if a preference should be greyed out # conditions used to determine if a preference should be greyed out
self.disable_when = { self.disable_when = {
@ -140,8 +140,7 @@ class Preferences:
_('Cygwin'), _('Cygwin'),
['List', ['List',
['File', 'cygwin_root', os.path.join(root, 'cygwin'), _('Root directory')], ['File', 'cygwin_root', os.path.join(root, 'cygwin'), _('Root directory')],
[ 'String', 'cygwin_cygdrive_prefix', '/cygdrive', _('Cygdrive prefix') ] ['String', 'cygwin_cygdrive_prefix', '/cygdrive', _('Cygdrive prefix')]]
]
]) ])
# create template for Version Control options # create template for Version Control options
@ -208,7 +207,9 @@ class Preferences:
if len(a) == 2 and p in self.bool_prefs: if len(a) == 2 and p in self.bool_prefs:
self.bool_prefs[p] = (a[1] == 'True') self.bool_prefs[p] = (a[1] == 'True')
elif len(a) == 2 and p in self.int_prefs: elif len(a) == 2 and p in self.int_prefs:
self.int_prefs[p] = max(self.int_prefs_min[p], min(int(a[1]), self.int_prefs_max[p])) self.int_prefs[p] = max(
self.int_prefs_min[p],
min(int(a[1]), self.int_prefs_max[p]))
elif len(a) == 2 and p in self.string_prefs: elif len(a) == 2 and p in self.string_prefs:
self.string_prefs[p] = a[1] self.string_prefs[p] = a[1]
else: else:
@ -263,10 +264,10 @@ class Preferences:
p, t = v p, t = v
if widgets[p].get_active() == t: if widgets[p].get_active() == t:
widgets[k].set_sensitive(False) widgets[k].set_sensitive(False)
dialog.vbox.add(w) # pylint: disable=no-member dialog.vbox.add(w)
w.show() w.show()
accept = (dialog.run() == Gtk.ResponseType.OK) # pylint: disable=no-member accept = (dialog.run() == Gtk.ResponseType.OK)
if accept: if accept:
for k in self.bool_prefs: for k in self.bool_prefs:
self.bool_prefs[k] = widgets[k].get_active() self.bool_prefs[k] = widgets[k].get_active()
@ -288,8 +289,7 @@ class Preferences:
ss.append(f'{k} "{v_escaped}"\n') ss.append(f'{k} "{v_escaped}"\n')
ss.sort() ss.sort()
with open(self.path, 'w', encoding='utf-8') as f: with open(self.path, 'w', encoding='utf-8') as f:
# pylint: disable-next=line-too-long f.write(f'# This prefs file was generated by {constants.APP_NAME} {constants.VERSION}.\n\n') # noqa: E501
f.write(f'# This prefs file was generated by {constants.APP_NAME} {constants.VERSION}.\n\n')
for s in ss: for s in ss:
f.write(s) f.write(s)
except IOError: except IOError:
@ -433,19 +433,19 @@ class Preferences:
s = os.sep.join(p) s = os.sep.join(p)
return s return s
# adaptor class to allow a Gtk.FontButton to be read like a Gtk.Entry # adaptor class to allow a Gtk.FontButton to be read like a Gtk.Entry
class _FontButton(Gtk.FontButton): class _FontButton(Gtk.FontButton):
def __init__(self): def __init__(self):
Gtk.FontButton.__init__(self) Gtk.FontButton.__init__(self)
def get_text(self): def get_text(self):
# pylint: disable=no-member
return self.get_font_name() return self.get_font_name()
# text entry widget with a button to help pick file names # text entry widget with a button to help pick file names
class _FileEntry(Gtk.Box): class _FileEntry(Gtk.Box):
def __init__(self, parent, title): def __init__(self, parent, title):
# pylint: disable=no-member
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL) Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
self.toplevel = parent self.toplevel = parent
self.title = title self.title = title
@ -463,7 +463,6 @@ class _FileEntry(Gtk.Box):
# action performed when the pick file button is pressed # action performed when the pick file button is pressed
def chooseFile(self, widget): def chooseFile(self, widget):
# pylint: disable=no-member
dialog = Gtk.FileChooserDialog( dialog = Gtk.FileChooserDialog(
self.title, self.title,
self.toplevel, self.toplevel,

View File

@ -29,13 +29,12 @@ import os
import re import re
import shlex import shlex
# pylint: disable=wrong-import-position from diffuse import utils
import gi import gi
gi.require_version('Gdk', '3.0') gi.require_version('Gdk', '3.0')
from gi.repository import Gdk from gi.repository import Gdk # noqa: E402
# pylint: enable=wrong-import-position
from diffuse import utils
class Resources: class Resources:
def __init__(self): def __init__(self):
@ -185,7 +184,8 @@ class Resources:
'edited': _Colour(0.5, 1.0, 0.5), 'edited': _Colour(0.5, 1.0, 0.5),
'preedit': _Colour(0.0, 0.0, 0.0), 'preedit': _Colour(0.0, 0.0, 0.0),
'text': _Colour(0.0, 0.0, 0.0), 'text': _Colour(0.0, 0.0, 0.0),
'text_background' : _Colour(1.0, 1.0, 1.0) } 'text_background': _Colour(1.0, 1.0, 1.0)
}
# default floats # default floats
self.floats = { self.floats = {
@ -194,7 +194,8 @@ class Resources:
'character_selection_opacity': 0.4, 'character_selection_opacity': 0.4,
'edited_opacity': 0.4, 'edited_opacity': 0.4,
'line_difference_opacity': 0.3, 'line_difference_opacity': 0.3,
'line_selection_opacity' : 0.4 } 'line_selection_opacity': 0.4
}
# default strings # default strings
self.strings = {} self.strings = {}
@ -222,7 +223,7 @@ class Resources:
elif token == 'Ctrl': elif token == 'Ctrl':
modifiers |= Gdk.ModifierType.CONTROL_MASK modifiers |= Gdk.ModifierType.CONTROL_MASK
elif token == 'Alt': elif token == 'Alt':
modifiers |= Gdk.ModifierType.MOD1_MASK # pylint: disable=no-member modifiers |= Gdk.ModifierType.MOD1_MASK
elif len(token) == 0 or token[0] == '_': elif len(token) == 0 or token[0] == '_':
raise ValueError() raise ValueError()
else: else:
@ -459,11 +460,11 @@ class Resources:
self.syntax_magic_patterns[key] = re.compile(args[2], flags) self.syntax_magic_patterns[key] = re.compile(args[2], flags)
else: else:
raise ValueError() raise ValueError()
# pylint: disable-next=bare-except
except: # Grr... the 're' module throws weird errors
# except ValueError: # except ValueError:
except: # noqa: E722 # Grr... the 're' module throws weird errors
utils.logError(_(f'Error processing line {i + 1} of {file_name}.')) utils.logError(_(f'Error processing line {i + 1} of {file_name}.'))
# colour resources # colour resources
class _Colour: class _Colour:
def __init__(self, r, g, b, a=1.0): def __init__(self, r, g, b, a=1.0):
@ -489,6 +490,7 @@ class _Colour:
def over(self, other): def over(self, other):
return self + other * (1 - self.alpha) return self + other * (1 - self.alpha)
# class to build and run a finite state machine for identifying syntax tokens # class to build and run a finite state machine for identifying syntax tokens
class _SyntaxParser: class _SyntaxParser:
# create a new state machine that begins in initial_state and classifies # create a new state machine that begins in initial_state and classifies
@ -536,4 +538,5 @@ class _SyntaxParser:
start = end start = end
return state_name, blocks return state_name, blocks
theResources = Resources() theResources = Resources()

View File

@ -23,14 +23,12 @@ import locale
import subprocess import subprocess
import traceback import traceback
# pylint: disable=wrong-import-position from diffuse import constants
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk # noqa: E402
# pylint: enable=wrong-import-position
# pylint: disable-next=no-name-in-module
from diffuse import constants
# convenience class for displaying a message dialogue # convenience class for displaying a message dialogue
class MessageDialog(Gtk.MessageDialog): class MessageDialog(Gtk.MessageDialog):
@ -48,6 +46,7 @@ class MessageDialog(Gtk.MessageDialog):
text=s) text=s)
self.set_title(constants.APP_NAME) self.set_title(constants.APP_NAME)
# widget to help pick an encoding # widget to help pick an encoding
class EncodingMenu(Gtk.Box): class EncodingMenu(Gtk.Box):
def __init__(self, prefs, autodetect=False): def __init__(self, prefs, autodetect=False):
@ -59,7 +58,7 @@ class EncodingMenu(Gtk.Box):
if autodetect: if autodetect:
self.encodings.insert(0, None) self.encodings.insert(0, None)
combobox.prepend_text(_('Auto Detect')) combobox.prepend_text(_('Auto Detect'))
self.pack_start(combobox, False, False, 0) # pylint: disable=no-member self.pack_start(combobox, False, False, 0)
combobox.show() combobox.show()
def set_text(self, encoding): def set_text(self, encoding):
@ -71,31 +70,37 @@ class EncodingMenu(Gtk.Box):
i = self.combobox.get_active() i = self.combobox.get_active()
return self.encodings[i] if i >= 0 else None return self.encodings[i] if i >= 0 else None
# platform test # platform test
def isWindows(): def isWindows():
return os.name == 'nt' return os.name == 'nt'
def _logPrintOutput(msg): def _logPrintOutput(msg):
if constants.log_print_output: if constants.log_print_output:
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
if constants.log_print_stack: if constants.log_print_stack:
traceback.print_stack() traceback.print_stack()
# convenience function to display debug messages # convenience function to display debug messages
def logDebug(msg): def logDebug(msg):
_logPrintOutput(f'DEBUG: {msg}') _logPrintOutput(f'DEBUG: {msg}')
# report error messages # report error messages
def logError(msg): def logError(msg):
_logPrintOutput(f'ERROR: {msg}') _logPrintOutput(f'ERROR: {msg}')
# report error messages and show dialog # report error messages and show dialog
def logErrorAndDialog(msg, parent=None): def logErrorAndDialog(msg, parent=None):
logError(msg) logError(msg)
dialog = MessageDialog(parent, Gtk.MessageType.ERROR, msg) dialog = MessageDialog(parent, Gtk.MessageType.ERROR, msg)
dialog.run() # pylint: disable=no-member dialog.run()
dialog.destroy() dialog.destroy()
# create nested subdirectories and return the complete path # create nested subdirectories and return the complete path
def make_subdirs(p, ss): def make_subdirs(p, ss):
for s in ss: for s in ss:
@ -107,6 +112,7 @@ def make_subdirs(p, ss):
pass pass
return p return p
# returns the Windows drive or share from a from an absolute path # returns the Windows drive or share from a from an absolute path
def _drive_from_path(path): def _drive_from_path(path):
d = path.split(os.sep) d = path.split(os.sep)
@ -114,6 +120,7 @@ def _drive_from_path(path):
return os.path.join(d[:4]) return os.path.join(d[:4])
return d[0] return d[0]
# constructs a relative path from 'a' to 'b', both should be absolute paths # constructs a relative path from 'a' to 'b', both should be absolute paths
def relpath(a, b): def relpath(a, b):
if isWindows(): if isWindows():
@ -128,6 +135,7 @@ def relpath(a, b):
r.extend(c2[i:]) r.extend(c2[i:])
return os.sep.join(r) return os.sep.join(r)
# helper function prevent files from being confused with command line options # helper function prevent files from being confused with command line options
# by prepending './' to the basename # by prepending './' to the basename
def safeRelativePath(abspath1, name, prefs, cygwin_pref): def safeRelativePath(abspath1, name, prefs, cygwin_pref):
@ -139,13 +147,16 @@ def safeRelativePath(abspath1, name, prefs, cygwin_pref):
s = s.replace('/', '\\') s = s.replace('/', '\\')
return s return s
# escape arguments for use with bash # escape arguments for use with bash
def _bash_escape(s): def _bash_escape(s):
return "'" + s.replace("'", "'\\''") + "'" return "'" + s.replace("'", "'\\''") + "'"
def _use_flatpak(): def _use_flatpak():
return constants.use_flatpak return constants.use_flatpak
# use popen to read the output of a command # use popen to read the output of a command
def popenRead(dn, cmd, prefs, bash_pref, success_results=None): def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
if success_results is None: if success_results is None:
@ -156,7 +167,8 @@ def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
prefs.convertToNativePath('/bin/bash.exe'), prefs.convertToNativePath('/bin/bash.exe'),
'-l', '-l',
'-c', '-c',
f"cd {_bash_escape(dn)}; {' '.join([ _bash_escape(arg) for arg in cmd ])}" ] f"cd {_bash_escape(dn)}; {' '.join([ _bash_escape(arg) for arg in cmd ])}"
]
dn = None dn = None
# use subprocess.Popen to retrieve the file contents # use subprocess.Popen to retrieve the file contents
if isWindows(): if isWindows():
@ -167,8 +179,7 @@ def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
info = None info = None
if _use_flatpak(): if _use_flatpak():
cmd = ['flatpak-spawn', '--host'] + cmd cmd = ['flatpak-spawn', '--host'] + cmd
with ( with (subprocess.Popen(
subprocess.Popen(
cmd, cmd,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -186,6 +197,7 @@ def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
raise IOError('Command failed.') raise IOError('Command failed.')
return s return s
# returns the number of characters in the string excluding any line ending # returns the number of characters in the string excluding any line ending
# characters # characters
def len_minus_line_ending(s): def len_minus_line_ending(s):
@ -198,29 +210,35 @@ def len_minus_line_ending(s):
n -= 1 n -= 1
return n return n
# returns the string without the line ending characters # returns the string without the line ending characters
def strip_eol(s): def strip_eol(s):
if s: if s:
s = s[:len_minus_line_ending(s)] s = s[:len_minus_line_ending(s)]
return s return s
# returns the list of strings without line ending characters # returns the list of strings without line ending characters
def _strip_eols(ss): def _strip_eols(ss):
return [strip_eol(s) for s in ss] return [strip_eol(s) for s in ss]
# use popen to read the output of a command # use popen to read the output of a command
def popenReadLines(dn, cmd, prefs, bash_pref, success_results=None): def popenReadLines(dn, cmd, prefs, bash_pref, success_results=None):
return _strip_eols(splitlines(popenRead( return _strip_eols(splitlines(popenRead(
dn, cmd, prefs, bash_pref, success_results).decode('utf-8', errors='ignore'))) dn, cmd, prefs, bash_pref, success_results).decode('utf-8', errors='ignore')))
def readconfiglines(fd): def readconfiglines(fd):
return fd.read().replace('\r', '').split('\n') return fd.read().replace('\r', '').split('\n')
# escape special glob characters # escape special glob characters
def globEscape(s): def globEscape(s):
m = {c: f'[{c}]' for c in '[]?*'} m = {c: f'[{c}]' for c in '[]?*'}
return ''.join([m.get(c, c) for c in s]) return ''.join([m.get(c, c) for c in s])
# split string into lines based upon DOS, Mac, and Unix line endings # split string into lines based upon DOS, Mac, and Unix line endings
def splitlines(text: str) -> list[str]: def splitlines(text: str) -> list[str]:
# split on new line characters # split on new line characters
@ -249,21 +267,25 @@ def splitlines(text: str) -> list[str]:
i = j i = j
return ss return ss
# also recognize old Mac OS line endings # also recognize old Mac OS line endings
def readlines(fd): def readlines(fd):
return _strip_eols(splitlines(fd.read())) return _strip_eols(splitlines(fd.read()))
# map an encoding name to its standard form # map an encoding name to its standard form
def norm_encoding(e): def norm_encoding(e):
if e is not None: if e is not None:
return e.replace('-', '_').lower() return e.replace('-', '_').lower()
return None return None
def null_to_empty(s): def null_to_empty(s):
if s is None: if s is None:
s = '' s = ''
return s return s
# utility method to step advance an adjustment # utility method to step advance an adjustment
def step_adjustment(adj, delta): def step_adjustment(adj, delta):
v = adj.get_value() + delta v = adj.get_value() + delta

View File

@ -23,6 +23,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# Bazaar support # Bazaar support
class Bzr(VcsInterface): class Bzr(VcsInterface):
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):

View File

@ -23,6 +23,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# CVS support # CVS support
class Cvs(VcsInterface): class Cvs(VcsInterface):
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):
@ -93,7 +94,8 @@ class Cvs(VcsInterface):
utils.safeRelativePath(self.root, name, prefs, 'cvs_cygwin') utils.safeRelativePath(self.root, name, prefs, 'cvs_cygwin')
], ],
prefs, prefs,
'cvs_bash'): 'cvs_bash'
):
if s.startswith(' Working revision:\t-'): if s.startswith(' Working revision:\t-'):
rev = s.split('\t')[1][1:] rev = s.split('\t')[1][1:]
return utils.popenRead( return utils.popenRead(

View File

@ -23,6 +23,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# Darcs support # Darcs support
class Darcs(VcsInterface): class Darcs(VcsInterface):
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):

View File

@ -19,6 +19,7 @@
import os import os
class FolderSet: class FolderSet:
'''Utility class to help support Git and Monotone. '''Utility class to help support Git and Monotone.
Represents a set of files and folders of interest for "git status" or Represents a set of files and folders of interest for "git status" or

View File

@ -23,6 +23,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# Git support # Git support
class Git(VcsInterface): class Git(VcsInterface):
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):

View File

@ -23,6 +23,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# Mercurial support # Mercurial support
class Hg(VcsInterface): class Hg(VcsInterface):
def __init__(self, root): def __init__(self, root):

View File

@ -24,6 +24,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# Monotone support # Monotone support
class Mtn(VcsInterface): class Mtn(VcsInterface):
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):
@ -96,7 +97,8 @@ class Mtn(VcsInterface):
self.root, self.root,
[vcs_bin, 'automate', 'get_manifest_of', prev], [vcs_bin, 'automate', 'get_manifest_of', prev],
prefs, prefs,
'mtn_bash'): 'mtn_bash'
):
s = shlex.split(s) s = shlex.split(s)
if len(s) > 1 and s[0] == 'dir': if len(s) > 1 and s[0] == 'dir':
removed_dirs.add(s[1]) removed_dirs.add(s[1])

View File

@ -22,6 +22,7 @@ import os
from diffuse import utils from diffuse import utils
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# RCS support # RCS support
class Rcs(VcsInterface): class Rcs(VcsInterface):
def getFileTemplate(self, prefs, name): def getFileTemplate(self, prefs, name):

View File

@ -22,6 +22,7 @@ import os
from diffuse import utils from diffuse import utils
from diffuse.vcs.svn import Svn from diffuse.vcs.svn import Svn
class Svk(Svn): class Svk(Svn):
@staticmethod @staticmethod
def _getVcs(): def _getVcs():

View File

@ -24,6 +24,7 @@ from diffuse import utils
from diffuse.vcs.folder_set import FolderSet from diffuse.vcs.folder_set import FolderSet
from diffuse.vcs.vcs_interface import VcsInterface from diffuse.vcs.vcs_interface import VcsInterface
# Subversion support # Subversion support
# SVK support subclasses from this # SVK support subclasses from this
class Svn(VcsInterface): class Svn(VcsInterface):

View File

@ -31,6 +31,7 @@ from diffuse.vcs.rcs import Rcs
from diffuse.vcs.svk import Svk from diffuse.vcs.svk import Svk
from diffuse.vcs.svn import Svn from diffuse.vcs.svn import Svn
class VcsRegistry: class VcsRegistry:
def __init__(self): def __init__(self):
# initialise the VCS objects # initialise the VCS objects
@ -74,17 +75,21 @@ def _find_parent_dir_with(path, dir_name):
break break
path = newpath path = newpath
def _get_bzr_repo(path, prefs): def _get_bzr_repo(path, prefs):
p = _find_parent_dir_with(path, '.bzr') p = _find_parent_dir_with(path, '.bzr')
return Bzr(p) if p else None return Bzr(p) if p else None
def _get_cvs_repo(path, prefs): def _get_cvs_repo(path, prefs):
return Cvs(path) if os.path.isdir(os.path.join(path, 'CVS')) else None return Cvs(path) if os.path.isdir(os.path.join(path, 'CVS')) else None
def _get_darcs_repo(path, prefs): def _get_darcs_repo(path, prefs):
p = _find_parent_dir_with(path, '_darcs') p = _find_parent_dir_with(path, '_darcs')
return Darcs(p) if p else None return Darcs(p) if p else None
def _get_git_repo(path, prefs): def _get_git_repo(path, prefs):
if 'GIT_DIR' in os.environ: if 'GIT_DIR' in os.environ:
try: try:
@ -127,14 +132,17 @@ def _get_git_repo(path, prefs):
break break
path = newpath path = newpath
def _get_hg_repo(path, prefs): def _get_hg_repo(path, prefs):
p = _find_parent_dir_with(path, '.hg') p = _find_parent_dir_with(path, '.hg')
return Hg(p) if p else None return Hg(p) if p else None
def _get_mtn_repo(path, prefs): def _get_mtn_repo(path, prefs):
p = _find_parent_dir_with(path, '_MTN') p = _find_parent_dir_with(path, '_MTN')
return Mtn(p) if p else None return Mtn(p) if p else None
def _get_rcs_repo(path, prefs): def _get_rcs_repo(path, prefs):
if os.path.isdir(os.path.join(path, 'RCS')): if os.path.isdir(os.path.join(path, 'RCS')):
return Rcs(path) return Rcs(path)
@ -151,10 +159,12 @@ def _get_rcs_repo(path, prefs):
pass pass
return None return None
def _get_svn_repo(path, prefs): def _get_svn_repo(path, prefs):
p = _find_parent_dir_with(path, '.svn') p = _find_parent_dir_with(path, '.svn')
return Svn(p) if p else None return Svn(p) if p else None
def _get_svk_repo(path, prefs): def _get_svk_repo(path, prefs):
name = path name = path
# parse the ~/.svk/config file to discover which directories are part of # parse the ~/.svk/config file to discover which directories are part of

View File

@ -21,18 +21,17 @@ import difflib
import os import os
import unicodedata import unicodedata
# pylint: disable=wrong-import-position from diffuse import utils
from diffuse.resources import theResources
import gi import gi
gi.require_version('GObject', '2.0') gi.require_version('GObject', '2.0')
gi.require_version('Gdk', '3.0') gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('Pango', '1.0') gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0') gi.require_version('PangoCairo', '1.0')
from gi.repository import GObject, Gdk, Gtk, Pango, PangoCairo from gi.repository import GObject, Gdk, Gtk, Pango, PangoCairo # noqa: E402
# pylint: enable=wrong-import-position
from diffuse import utils
from diffuse.resources import theResources
# mapping to column width of a character (tab will never be in this map) # mapping to column width of a character (tab will never be in this map)
_char_width_cache = {} _char_width_cache = {}
@ -43,6 +42,7 @@ LINE_MODE = 0
CHAR_MODE = 1 CHAR_MODE = 1
ALIGN_MODE = 2 ALIGN_MODE = 2
# This is a replacement for Gtk.ScrolledWindow as it forced expose events to be # This is a replacement for Gtk.ScrolledWindow as it forced expose events to be
# handled immediately after changing the viewport position. This could cause # handled immediately after changing the viewport position. This could cause
# the application to become unresponsive for a while as it processed a large # the application to become unresponsive for a while as it processed a large
@ -140,9 +140,9 @@ class ScrolledWindow(Gtk.Grid):
self.partial_redraw = True self.partial_redraw = True
self.darea_queue_draw_area(x, y, w, h) self.darea_queue_draw_area(x, y, w, h)
# widget used to compare and merge text files # widget used to compare and merge text files
class FileDiffViewer(Gtk.Grid): class FileDiffViewer(Gtk.Grid):
# pylint: disable=too-many-public-methods
# class describing a text pane # class describing a text pane
class Pane: class Pane:
def __init__(self): def __init__(self):
@ -254,7 +254,8 @@ class FileDiffViewer(Gtk.Grid):
'copy_left_into_selection': self.copy_left_into_selection, 'copy_left_into_selection': self.copy_left_into_selection,
'copy_right_into_selection': self.copy_right_into_selection, 'copy_right_into_selection': self.copy_right_into_selection,
'merge_from_left_then_right': self.merge_from_left_then_right, 'merge_from_left_then_right': self.merge_from_left_then_right,
'merge_from_right_then_left': self.merge_from_right_then_left } 'merge_from_right_then_left': self.merge_from_right_then_left
}
self._align_mode_actions = { self._align_mode_actions = {
'enter_line_mode': self._align_mode_enter_line_mode, 'enter_line_mode': self._align_mode_enter_line_mode,
'enter_character_mode': self.setCharMode, 'enter_character_mode': self.setCharMode,
@ -266,9 +267,11 @@ class FileDiffViewer(Gtk.Grid):
'right': self._line_mode_right, 'right': self._line_mode_right,
'page_up': self._line_mode_page_up, 'page_up': self._line_mode_page_up,
'page_down': self._line_mode_page_down, 'page_down': self._line_mode_page_down,
'align': self._align_text } 'align': self._align_text
}
self._character_mode_actions = { self._character_mode_actions = {
'enter_line_mode': self.setLineMode } 'enter_line_mode': self.setLineMode
}
self._button_actions = { self._button_actions = {
'undo': self.undo, 'undo': self.undo,
'redo': self.redo, 'redo': self.redo,
@ -303,7 +306,8 @@ class FileDiffViewer(Gtk.Grid):
'copy_left_into_selection': self.copy_left_into_selection, 'copy_left_into_selection': self.copy_left_into_selection,
'copy_right_into_selection': self.copy_right_into_selection, 'copy_right_into_selection': self.copy_right_into_selection,
'merge_from_left_then_right': self.merge_from_left_then_right, 'merge_from_left_then_right': self.merge_from_left_then_right,
'merge_from_right_then_left': self.merge_from_right_then_left } 'merge_from_right_then_left': self.merge_from_right_then_left
}
# create panes # create panes
self.dareas = [] self.dareas = []
@ -343,7 +347,6 @@ class FileDiffViewer(Gtk.Grid):
self.attach(diffmap, n, 1, 1, 1) self.attach(diffmap, n, 1, 1, 1)
diffmap.show() diffmap.show()
diffmap.set_size_request(16 * n, 0) diffmap.set_size_request(16 * n, 0)
# pylint: disable-next=no-member
self.add_events(Gdk.EventMask.KEY_PRESS_MASK | self.add_events(Gdk.EventMask.KEY_PRESS_MASK |
Gdk.EventMask.FOCUS_CHANGE_MASK) Gdk.EventMask.FOCUS_CHANGE_MASK)
self.connect('focus-in-event', self.focus_in_cb) self.connect('focus-in-event', self.focus_in_cb)
@ -372,7 +375,7 @@ class FileDiffViewer(Gtk.Grid):
# this must be connected with 'connect_after()' so the final widget sizes # this must be connected with 'connect_after()' so the final widget sizes
# are known and the scroll bar can be moved to the first difference # are known and the scroll bar can be moved to the first difference
def _realise_cb(self, widget): def _realise_cb(self, widget):
self.im_context.set_client_window(self.get_window()) # pylint: disable=no-member self.im_context.set_client_window(self.get_window())
try: try:
self.go_to_line(self.options['line']) self.go_to_line(self.options['line'])
except KeyError: except KeyError:
@ -1128,7 +1131,7 @@ class FileDiffViewer(Gtk.Grid):
# #
# advance one row at a time inserting spacer lines as we go # advance one row at a time inserting spacer lines as we go
# 'i' indicates which row we are processing # 'i' indicates which row we are processing
# 'k' indicates which pair of neighbours we are processing # 'k' indicates which pair of neighbors we are processing
i, k = 0, 0 i, k = 0, 0
bi = [0, 0] bi = [0, 0]
bn = [0, 0] bn = [0, 0]
@ -1606,7 +1609,6 @@ class FileDiffViewer(Gtk.Grid):
x -= int(self.hadj.get_value()) x -= int(self.hadj.get_value())
y -= int(self.vadj.get_value()) y -= int(self.vadj.get_value())
# translate to a position relative to the window # translate to a position relative to the window
# pylint: disable=no-member
x, y = self.dareas[self.current_pane].translate_coordinates(self.get_toplevel(), x, y) x, y = self.dareas[self.current_pane].translate_coordinates(self.get_toplevel(), x, y)
# input methods support widgets are centred horizontally about the # input methods support widgets are centred horizontally about the
# cursor, a width of 50 seems to give a better widget positions # cursor, a width of 50 seems to give a better widget positions
@ -1771,14 +1773,14 @@ class FileDiffViewer(Gtk.Grid):
# callback for mouse button presses in the text window # callback for mouse button presses in the text window
def darea_button_press_cb(self, widget, event, f): def darea_button_press_cb(self, widget, event, f):
self.get_toplevel().set_focus(self) # pylint: disable=no-member self.get_toplevel().set_focus(self)
x = int(event.x + self.hadj.get_value()) x = int(event.x + self.hadj.get_value())
y = int(event.y + self.vadj.get_value()) y = int(event.y + self.vadj.get_value())
nlines = len(self.panes[f].lines) nlines = len(self.panes[f].lines)
i = min(y // self.font_height, nlines) i = min(y // self.font_height, nlines)
if event.button == 1: if event.button == 1:
# left mouse button # left mouse button
if event.type == Gdk.EventType._2BUTTON_PRESS: # pylint: disable=no-member,protected-access if event.type == Gdk.EventType._2BUTTON_PRESS:
# double click # double click
if self.mode == ALIGN_MODE: if self.mode == ALIGN_MODE:
self.setLineMode() self.setLineMode()
@ -1804,7 +1806,7 @@ class FileDiffViewer(Gtk.Grid):
while j < n and _get_character_class(text[j]) == c: while j < n and _get_character_class(text[j]) == c:
j += 1 j += 1
self.setCurrentChar(i, j, i, k) self.setCurrentChar(i, j, i, k)
elif event.type == Gdk.EventType._3BUTTON_PRESS: # pylint: disable=no-member,protected-access elif event.type == Gdk.EventType._3BUTTON_PRESS:
# triple click, select a whole line # triple click, select a whole line
if self.mode == CHAR_MODE and self.current_pane == f: if self.mode == CHAR_MODE and self.current_pane == f:
i2 = min(i + 1, nlines) i2 = min(i + 1, nlines)
@ -1833,9 +1835,8 @@ class FileDiffViewer(Gtk.Grid):
can_select = self.mode in (LINE_MODE, CHAR_MODE) and f == self.current_pane can_select = self.mode in (LINE_MODE, CHAR_MODE) and f == self.current_pane
can_swap = (f != self.current_pane) can_swap = (f != self.current_pane)
# pylint: disable=line-too-long menu = createMenu([
menu = createMenu( [_('Align with Selection'), self.align_with_selection_cb, [f, i], Gtk.STOCK_EXECUTE, None, can_align], # noqa: E501
[ [_('Align with Selection'), self.align_with_selection_cb, [f, i], Gtk.STOCK_EXECUTE, None, can_align],
[_('Isolate'), self.button_cb, 'isolate', None, None, can_isolate], [_('Isolate'), self.button_cb, 'isolate', None, None, can_isolate],
[_('Merge Selection'), self.merge_lines_cb, f, None, None, can_merge], [_('Merge Selection'), self.merge_lines_cb, f, None, None, can_merge],
[], [],
@ -1844,10 +1845,10 @@ class FileDiffViewer(Gtk.Grid):
[_('Paste'), self.button_cb, 'paste', Gtk.STOCK_PASTE, None, can_select], [_('Paste'), self.button_cb, 'paste', Gtk.STOCK_PASTE, None, can_select],
[], [],
[_('Select All'), self.button_cb, 'select_all', None, None, can_select], [_('Select All'), self.button_cb, 'select_all', None, None, can_select],
[_('Clear Edits'), self.button_cb, 'clear_edits', Gtk.STOCK_CLEAR, None, can_isolate], [_('Clear Edits'), self.button_cb, 'clear_edits', Gtk.STOCK_CLEAR, None, can_isolate], # noqa: E501
[], [],
[_('Swap with Selected Pane'), self.swap_panes_cb, f, None, None, can_swap] ]) [_('Swap with Selected Pane'), self.swap_panes_cb, f, None, None, can_swap]
# pylint: enable=line-too-long ])
menu.attach_to_widget(self) menu.attach_to_widget(self)
menu.popup(None, None, None, None, event.button, event.time) menu.popup(None, None, None, None, event.button, event.time)
@ -1996,7 +1997,8 @@ class FileDiffViewer(Gtk.Grid):
diffcolours = [ diffcolours = [
theResources.getDifferenceColour(f), theResources.getDifferenceColour(f),
theResources.getDifferenceColour(f + 1) ] theResources.getDifferenceColour(f + 1)
]
diffcolours.append((diffcolours[0] + diffcolours[1]) * 0.5) diffcolours.append((diffcolours[0] + diffcolours[1]) * 0.5)
# iterate over each exposed line # iterate over each exposed line
@ -2419,7 +2421,8 @@ class FileDiffViewer(Gtk.Grid):
for f in range(n): for f in range(n):
diffcolours = [ diffcolours = [
theResources.getDifferenceColour(f), theResources.getDifferenceColour(f),
theResources.getDifferenceColour(f + 1) ] theResources.getDifferenceColour(f + 1)
]
diffcolours.append((diffcolours[0] + diffcolours[1]) * 0.5) diffcolours.append((diffcolours[0] + diffcolours[1]) * 0.5)
wx = f * wn wx = f * wn
# draw in two passes, more important stuff in the second pass # draw in two passes, more important stuff in the second pass
@ -3632,8 +3635,8 @@ class FileDiffViewer(Gtk.Grid):
# join and remove null lines # join and remove null lines
b.extend(b) b.extend(b)
for l, s in zip(lines, spaces): for line, space in zip(lines, spaces):
l.extend(s) line.extend(space)
_remove_null_lines(b, lines) _remove_null_lines(b, lines)
# replace f's lines with original, growing if necessary # replace f's lines with original, growing if necessary
@ -3686,6 +3689,7 @@ class FileDiffViewer(Gtk.Grid):
def merge_from_right_then_left(self): def merge_from_right_then_left(self):
self._mergeBoth(True) self._mergeBoth(True)
# convenience method for creating a menu according to a template # convenience method for creating a menu according to a template
def createMenu(specs, radio=None, accel_group=None): def createMenu(specs, radio=None, accel_group=None):
menu = Gtk.Menu.new() menu = Gtk.Menu.new()
@ -3707,7 +3711,7 @@ def createMenu(specs, radio=None, accel_group=None):
item.connect('activate', cb, data) item.connect('activate', cb, data)
if len(spec) > 3 and spec[3] is not None: if len(spec) > 3 and spec[3] is not None:
image = Gtk.Image.new() image = Gtk.Image.new()
image.set_from_stock(spec[3], Gtk.IconSize.MENU) # pylint: disable=no-member image.set_from_stock(spec[3], Gtk.IconSize.MENU)
item.set_image(image) item.set_image(image)
if accel_group is not None and len(spec) > 4: if accel_group is not None and len(spec) > 4:
a = theResources.getKeyBindings('menu', spec[4]) a = theResources.getKeyBindings('menu', spec[4])
@ -3730,10 +3734,12 @@ def createMenu(specs, radio=None, accel_group=None):
menu.append(item) menu.append(item)
return menu return menu
ALPHANUMERIC_CLASS = 0 ALPHANUMERIC_CLASS = 0
WHITESPACE_CLASS = 1 WHITESPACE_CLASS = 1
OTHER_CLASS = 2 OTHER_CLASS = 2
# maps similar types of characters to a group # maps similar types of characters to a group
def _get_character_class(c): def _get_character_class(c):
if c.isalnum() or c == '_': if c.isalnum() or c == '_':
@ -3742,6 +3748,7 @@ def _get_character_class(c):
return WHITESPACE_CLASS return WHITESPACE_CLASS
return OTHER_CLASS return OTHER_CLASS
# patience diff with difflib-style fallback # patience diff with difflib-style fallback
def _patience_diff(a, b): def _patience_diff(a, b):
matches, len_a, len_b = [], len(a), len(b) matches, len_a, len_b = [], len(a), len(b)
@ -3822,6 +3829,7 @@ def _patience_diff(a, b):
matches.append((len_a, len_b, 0)) matches.append((len_a, len_b, 0))
return matches return matches
# longest common subsequence of unique elements common to 'a' and 'b' # longest common subsequence of unique elements common to 'a' and 'b'
def _patience_subsequence(a, b): def _patience_subsequence(a, b):
# value unique lines by their order in each list # value unique lines by their order in each list
@ -3877,6 +3885,7 @@ def _patience_subsequence(a, b):
result.reverse() result.reverse()
return result return result
# difflib-style approximation of the longest common subsequence # difflib-style approximation of the longest common subsequence
def _lcs_approx(a, b): def _lcs_approx(a, b):
count1, lookup = {}, {} count1, lookup = {}, {}
@ -3950,18 +3959,22 @@ def _lcs_approx(a, b):
return aidx, bidx, nidx return aidx, bidx, nidx
return None return None
# True if the string ends with '\r\n' # True if the string ends with '\r\n'
def _has_dos_line_ending(s): def _has_dos_line_ending(s):
return s.endswith('\r\n') return s.endswith('\r\n')
# True if the string ends with '\r' # True if the string ends with '\r'
def _has_mac_line_ending(s): def _has_mac_line_ending(s):
return s.endswith('\r') return s.endswith('\r')
# True if the string ends with '\n' but not '\r\n' # True if the string ends with '\n' but not '\r\n'
def _has_unix_line_ending(s): def _has_unix_line_ending(s):
return s.endswith('\n') and not s.endswith('\r\n') return s.endswith('\n') and not s.endswith('\r\n')
# returns the format mask for a list of strings # returns the format mask for a list of strings
def _get_format(ss): def _get_format(ss):
flags = 0 flags = 0
@ -3975,6 +3988,7 @@ def _get_format(ss):
flags |= utils.UNIX_FORMAT flags |= utils.UNIX_FORMAT
return flags return flags
# convenience method to change the line ending of a string # convenience method to change the line ending of a string
def _convert_to_format(s, fmt): def _convert_to_format(s, fmt):
if s is not None and fmt != 0: if s is not None and fmt != 0:
@ -3996,6 +4010,7 @@ def _convert_to_format(s, fmt):
s += '\r' s += '\r'
return s return s
# Enforcing manual alignment is accomplished by dividing the lines of text into # Enforcing manual alignment is accomplished by dividing the lines of text into
# sections that are matched independently. 'blocks' is an array of integers # sections that are matched independently. 'blocks' is an array of integers
# describing how many lines (including null lines for spacing) that are in each # describing how many lines (including null lines for spacing) that are in each
@ -4005,12 +4020,12 @@ def _convert_to_format(s, fmt):
# in this array so 'blocks' will be an empty array when there are no lines. A # in this array so 'blocks' will be an empty array when there are no lines. A
# 'cut' at location 'i' means a line 'i-1' and line 'i' belong to different # 'cut' at location 'i' means a line 'i-1' and line 'i' belong to different
# sections # sections
def _create_block(n): def _create_block(n):
if n > 0: if n > 0:
return [n] return [n]
return [] return []
# returns the two sets of blocks after cutting at 'i' # returns the two sets of blocks after cutting at 'i'
def _cut_blocks(i, blocks): def _cut_blocks(i, blocks):
pre, post, nlines = [], [], 0 pre, post, nlines = [], [], 0
@ -4026,6 +4041,7 @@ def _cut_blocks(i, blocks):
nlines += b nlines += b
return pre, post return pre, post
# returns a set of blocks containing all of the cuts in the inputs # returns a set of blocks containing all of the cuts in the inputs
def _merge_blocks(leftblocks, rightblocks): def _merge_blocks(leftblocks, rightblocks):
leftblocks, rightblocks, b = leftblocks[:], rightblocks[:], [] leftblocks, rightblocks, b = leftblocks[:], rightblocks[:], []
@ -4043,6 +4059,7 @@ def _merge_blocks(leftblocks, rightblocks):
b.append(n) b.append(n)
return b return b
# utility method to simplify working with structures used to describe character # utility method to simplify working with structures used to describe character
# differences of a line # differences of a line
# #
@ -4078,6 +4095,7 @@ def _merge_ranges(r1, r2):
result.extend(r2) result.extend(r2)
return result return result
# eliminates lines that are spacing lines in all panes # eliminates lines that are spacing lines in all panes
def _remove_null_lines(blocks, lines_set): def _remove_null_lines(blocks, lines_set):
bi, bn, i = 0, 0, 0 bi, bn, i = 0, 0, 0
@ -4097,22 +4115,23 @@ def _remove_null_lines(blocks, lines_set):
bn += blocks[bi] bn += blocks[bi]
bi += 1 bi += 1
# returns true if the string only contains whitespace characters # returns true if the string only contains whitespace characters
def _is_blank(s): def _is_blank(s):
for c in utils.whitespace: for c in utils.whitespace:
s = s.replace(c, '') s = s.replace(c, '')
return len(s) == 0 return len(s) == 0
# use Pango.SCALE instead of Pango.PIXELS to avoid overflow exception # use Pango.SCALE instead of Pango.PIXELS to avoid overflow exception
def _pixels(size): def _pixels(size):
return int(size / Pango.SCALE + 0.5) return int(size / Pango.SCALE + 0.5)
# create 'title_changed' signal for FileDiffViewer # create 'title_changed' signal for FileDiffViewer
# pylint: disable=line-too-long GObject.signal_new('swapped-panes', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int)) # noqa: E501
GObject.signal_new('swapped-panes', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int)) GObject.signal_new('num-edits-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, )) # noqa: E501
GObject.signal_new('num-edits-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, )) GObject.signal_new('mode-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()) # noqa: E501
GObject.signal_new('mode-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()) GObject.signal_new('cursor-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()) # noqa: E501
GObject.signal_new('cursor-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()) GObject.signal_new('syntax-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (str, )) # noqa: E501
GObject.signal_new('syntax-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (str, )) GObject.signal_new('format-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int)) # noqa: E501
GObject.signal_new('format-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int))
# pylint: enable=line-too-long