Merge pull request #125 from MightyCreak/flake8
Use Flake8 instead of PyLint + integrate MyPy
This commit is contained in:
commit
6a90c81a9c
|
@ -0,0 +1,6 @@
|
||||||
|
[flake8]
|
||||||
|
builtins = _
|
||||||
|
max-line-length = 100
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
exclude = src/diffuse/main.py
|
|
@ -17,20 +17,21 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
- uses: actions/setup-python@v2
|
||||||
fetch-depth: 0
|
- run: sudo apt install libgirepository1.0-dev
|
||||||
|
- run: pip install -r requirements.txt
|
||||||
|
|
||||||
- name: Pylint
|
- name: Flake8
|
||||||
uses: cclauss/GitHub-Action-for-pylint@master
|
run: flake8 src/
|
||||||
with:
|
|
||||||
args: "apk add --no-cache gtk+3.0-dev gobject-introspection-dev ; pip install -r requirements.txt ; pylint src/**/*.py"
|
- name: MyPy
|
||||||
|
run: mypy src/
|
||||||
|
|
||||||
meson-build-test:
|
meson-build-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
- uses: actions/setup-python@v2
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt-get -y install appstream appstream-util desktop-file-utils gettext
|
run: sudo apt-get -y install appstream appstream-util desktop-file-utils gettext
|
||||||
|
@ -52,6 +53,7 @@ jobs:
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
- name: Flatpak builder
|
- name: Flatpak builder
|
||||||
uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v3
|
uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
flake8 ~= 3.8
|
||||||
|
mypy ~= 0.910
|
||||||
PyGObject ~= 3.40
|
PyGObject ~= 3.40
|
||||||
pylint~=2.11
|
|
||||||
|
|
|
@ -19,17 +19,14 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
from diffuse import constants # type: ignore
|
||||||
import gi
|
from diffuse import utils
|
||||||
|
|
||||||
|
import gi # type: ignore
|
||||||
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 # type: ignore # 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
|
||||||
|
|
|
@ -25,29 +25,26 @@ import shlex
|
||||||
import stat
|
import stat
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
from urllib.parse import urlparse
|
||||||
import gi
|
|
||||||
|
from diffuse import constants # type: ignore
|
||||||
|
from diffuse import utils
|
||||||
|
from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog
|
||||||
|
from diffuse.preferences import Preferences
|
||||||
|
from diffuse.resources import theResources
|
||||||
|
from diffuse.vcs.vcs_registry import VcsRegistry
|
||||||
|
from diffuse.widgets import FileDiffViewerBase
|
||||||
|
from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE
|
||||||
|
|
||||||
|
import gi # type: ignore
|
||||||
gi.require_version('GObject', '2.0')
|
gi.require_version('GObject', '2.0')
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
gi.require_version('Gdk', '3.0')
|
gi.require_version('Gdk', '3.0')
|
||||||
gi.require_version('GdkPixbuf', '2.0')
|
gi.require_version('GdkPixbuf', '2.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, Gtk, Gdk, GdkPixbuf, Pango, PangoCairo
|
from gi.repository import GObject, Gtk, Gdk, GdkPixbuf, Pango, PangoCairo # type: ignore # noqa: E402
|
||||||
# pylint: enable=wrong-import-position
|
|
||||||
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
# pylint: disable-next=no-name-in-module
|
|
||||||
from diffuse import constants
|
|
||||||
|
|
||||||
from diffuse import utils
|
|
||||||
from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog
|
|
||||||
from diffuse.preferences import Preferences
|
|
||||||
from diffuse.resources import theResources
|
|
||||||
from diffuse.vcs.vcs_registry import VcsRegistry
|
|
||||||
from diffuse.widgets import FileDiffViewer
|
|
||||||
from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE
|
|
||||||
|
|
||||||
theVCSs = VcsRegistry()
|
theVCSs = VcsRegistry()
|
||||||
|
|
||||||
|
@ -113,8 +110,8 @@ class FileInfo:
|
||||||
# this class displays tab for switching between viewers and dispatches menu
|
# this class displays tab for switching between viewers and dispatches menu
|
||||||
# commands to the current viewer
|
# commands to the current viewer
|
||||||
class Diffuse(Gtk.Window):
|
class Diffuse(Gtk.Window):
|
||||||
# specialisation of FileDiffViewer for Diffuse
|
# specialization of FileDiffViewerBase for Diffuse
|
||||||
class FileDiffViewer(FileDiffViewer):
|
class FileDiffViewer(FileDiffViewerBase):
|
||||||
# pane header
|
# pane header
|
||||||
class PaneHeader(Gtk.Box):
|
class PaneHeader(Gtk.Box):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -226,7 +223,7 @@ class Diffuse(Gtk.Window):
|
||||||
self.encoding.set_text(s)
|
self.encoding.set_text(s)
|
||||||
|
|
||||||
def __init__(self, n, prefs, title):
|
def __init__(self, n, prefs, title):
|
||||||
FileDiffViewer.__init__(self, n, prefs)
|
FileDiffViewerBase.__init__(self, n, prefs)
|
||||||
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.status = ''
|
self.status = ''
|
||||||
|
@ -534,8 +531,7 @@ class Diffuse(Gtk.Window):
|
||||||
info.name, info.encoding, info.revision, info.label = name, encoding, None, label
|
info.name, info.encoding, info.revision, info.label = name, encoding, None, label
|
||||||
info.last_stat = info.stat = os.stat(name)
|
info.last_stat = info.stat = os.stat(name)
|
||||||
self.setFileInfo(f, info)
|
self.setFileInfo(f, info)
|
||||||
# update the syntax highlighting incase we changed the file
|
# update the syntax highlighting in case we changed the file extension
|
||||||
# extension
|
|
||||||
syntax = theResources.guessSyntaxForFile(name, ss)
|
syntax = theResources.guessSyntaxForFile(name, ss)
|
||||||
if syntax is not None:
|
if syntax is not None:
|
||||||
self.setSyntax(syntax)
|
self.setSyntax(syntax)
|
||||||
|
@ -987,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)
|
||||||
|
|
|
@ -23,17 +23,14 @@ import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
from diffuse import constants # type: ignore
|
||||||
import gi
|
|
||||||
gi.require_version('Gtk', '3.0')
|
|
||||||
from gi.repository import Gtk
|
|
||||||
# pylint: enable=wrong-import-position
|
|
||||||
|
|
||||||
# pylint: disable-next=no-name-in-module
|
|
||||||
from diffuse import constants
|
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
|
|
||||||
|
import gi # type: ignore
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk # type: ignore # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
# 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:
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -29,14 +29,13 @@ import os
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
|
||||||
import gi
|
|
||||||
gi.require_version('Gdk', '3.0')
|
|
||||||
from gi.repository import Gdk
|
|
||||||
# pylint: enable=wrong-import-position
|
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
|
|
||||||
|
import gi # type: ignore
|
||||||
|
gi.require_version('Gdk', '3.0')
|
||||||
|
from gi.repository import Gdk # type: ignore # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
class Resources:
|
class Resources:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# default keybindings
|
# default keybindings
|
||||||
|
@ -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()
|
||||||
|
|
|
@ -23,14 +23,12 @@ import locale
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
from diffuse import constants # type: ignore
|
||||||
import gi
|
|
||||||
gi.require_version('Gtk', '3.0')
|
import gi # type: ignore
|
||||||
from gi.repository import Gtk
|
gi.require_version('Gtk', '3.0')
|
||||||
# pylint: enable=wrong-import-position
|
from gi.repository import Gtk # type: ignore # noqa: E402
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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])
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -21,28 +21,30 @@ import difflib
|
||||||
import os
|
import os
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
from typing import Dict
|
||||||
import gi
|
|
||||||
|
from diffuse import utils
|
||||||
|
from diffuse.resources import theResources
|
||||||
|
|
||||||
|
import gi # type: ignore
|
||||||
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 # type: ignore # 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: Dict[str, str] = {}
|
||||||
|
|
||||||
# the file diff viewer is always in one of these modes defining the cursor,
|
# the file diff viewer is always in one of these modes defining the cursor,
|
||||||
# and hotkey behaviour
|
# and hotkey behavior
|
||||||
LINE_MODE = 0
|
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 +142,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 FileDiffViewerBase(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 +256,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 +269,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 +308,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 = []
|
||||||
|
@ -311,7 +317,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
self.hadj = Gtk.Adjustment.new(0, 0, 0, 0, 0, 0)
|
self.hadj = Gtk.Adjustment.new(0, 0, 0, 0, 0, 0)
|
||||||
self.vadj = Gtk.Adjustment.new(0, 0, 0, 0, 0, 0)
|
self.vadj = Gtk.Adjustment.new(0, 0, 0, 0, 0, 0)
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
pane = FileDiffViewer.Pane()
|
pane = FileDiffViewerBase.Pane()
|
||||||
self.panes.append(pane)
|
self.panes.append(pane)
|
||||||
|
|
||||||
# pane contents
|
# pane contents
|
||||||
|
@ -343,7 +349,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 +377,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:
|
||||||
|
@ -709,7 +714,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
pane = self.panes[f]
|
pane = self.panes[f]
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.SetFormatUndo(f, fmt, pane.format))
|
self.addUndo(FileDiffViewerBase.SetFormatUndo(f, fmt, pane.format))
|
||||||
pane.format = fmt
|
pane.format = fmt
|
||||||
self.emit('format_changed', f, fmt)
|
self.emit('format_changed', f, fmt)
|
||||||
|
|
||||||
|
@ -731,12 +736,12 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def instanceLine(self, f, i, reverse=False):
|
def instanceLine(self, f, i, reverse=False):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.InstanceLineUndo(f, i, reverse))
|
self.addUndo(FileDiffViewerBase.InstanceLineUndo(f, i, reverse))
|
||||||
pane = self.panes[f]
|
pane = self.panes[f]
|
||||||
if reverse:
|
if reverse:
|
||||||
pane.lines[i] = None
|
pane.lines[i] = None
|
||||||
else:
|
else:
|
||||||
line = FileDiffViewer.Line()
|
line = FileDiffViewerBase.Line()
|
||||||
pane.lines[i] = line
|
pane.lines[i] = line
|
||||||
|
|
||||||
# Undo for changing the text for a Line object
|
# Undo for changing the text for a Line object
|
||||||
|
@ -771,7 +776,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
flags = self.getMapFlags(f, i)
|
flags = self.getMapFlags(f, i)
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.UpdateLineTextUndo(
|
self.addUndo(FileDiffViewerBase.UpdateLineTextUndo(
|
||||||
f,
|
f,
|
||||||
i,
|
i,
|
||||||
line.is_modified,
|
line.is_modified,
|
||||||
|
@ -832,7 +837,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def insertNull(self, f, i, reverse):
|
def insertNull(self, f, i, reverse):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.InsertNullUndo(f, i, reverse))
|
self.addUndo(FileDiffViewerBase.InsertNullUndo(f, i, reverse))
|
||||||
pane = self.panes[f]
|
pane = self.panes[f]
|
||||||
lines = pane.lines
|
lines = pane.lines
|
||||||
# update/invalidate all relevant caches
|
# update/invalidate all relevant caches
|
||||||
|
@ -863,7 +868,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def invalidateLineMatching(self, i, n, new_n):
|
def invalidateLineMatching(self, i, n, new_n):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.InvalidateLineMatchingUndo(i, n, new_n))
|
self.addUndo(FileDiffViewerBase.InvalidateLineMatchingUndo(i, n, new_n))
|
||||||
# update/invalidate all relevant caches and queue widgets for redraw
|
# update/invalidate all relevant caches and queue widgets for redraw
|
||||||
i2 = i + n
|
i2 = i + n
|
||||||
for f, pane in enumerate(self.panes):
|
for f, pane in enumerate(self.panes):
|
||||||
|
@ -893,7 +898,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def alignmentChange(self, finished):
|
def alignmentChange(self, finished):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.AlignmentChangeUndo(finished))
|
self.addUndo(FileDiffViewerBase.AlignmentChangeUndo(finished))
|
||||||
if finished:
|
if finished:
|
||||||
self.updateSize(False)
|
self.updateSize(False)
|
||||||
|
|
||||||
|
@ -936,7 +941,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def updateBlocks(self, blocks):
|
def updateBlocks(self, blocks):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.UpdateBlocksUndo(self.blocks, blocks))
|
self.addUndo(FileDiffViewerBase.UpdateBlocksUndo(self.blocks, blocks))
|
||||||
self.blocks = blocks
|
self.blocks = blocks
|
||||||
|
|
||||||
# insert 'n' blank lines in all panes
|
# insert 'n' blank lines in all panes
|
||||||
|
@ -1028,7 +1033,8 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def replaceLines(self, f, lines, new_lines, max_num, new_max_num):
|
def replaceLines(self, f, lines, new_lines, max_num, new_max_num):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
# create an Undo object for the action
|
# create an Undo object for the action
|
||||||
self.addUndo(FileDiffViewer.ReplaceLinesUndo(f, lines, new_lines, max_num, new_max_num))
|
self.addUndo(FileDiffViewerBase.ReplaceLinesUndo(
|
||||||
|
f, lines, new_lines, max_num, new_max_num))
|
||||||
pane = self.panes[f]
|
pane = self.panes[f]
|
||||||
pane.lines = new_lines
|
pane.lines = new_lines
|
||||||
# update/invalidate all relevant caches and queue widgets for redraw
|
# update/invalidate all relevant caches and queue widgets for redraw
|
||||||
|
@ -1128,7 +1134,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]
|
||||||
|
@ -1195,7 +1201,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
if n > 0:
|
if n > 0:
|
||||||
blocks.append(n)
|
blocks.append(n)
|
||||||
# create line objects for the text
|
# create line objects for the text
|
||||||
Line = FileDiffViewer.Line
|
Line = FileDiffViewerBase.Line
|
||||||
mid = [[Line(j + 1, ss[j]) for j in range(n)]]
|
mid = [[Line(j + 1, ss[j]) for j in range(n)]]
|
||||||
|
|
||||||
if f > 0:
|
if f > 0:
|
||||||
|
@ -1253,7 +1259,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
lines.append(None)
|
lines.append(None)
|
||||||
else:
|
else:
|
||||||
line_num += 1
|
line_num += 1
|
||||||
lines.append(FileDiffViewer.Line(line_num, s))
|
lines.append(FileDiffViewerBase.Line(line_num, s))
|
||||||
|
|
||||||
# update loaded pane
|
# update loaded pane
|
||||||
self.replaceLines(f, pane.lines, lines, pane.max_line_number, line_num)
|
self.replaceLines(f, pane.lines, lines, pane.max_line_number, line_num)
|
||||||
|
@ -1487,7 +1493,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
# selection
|
# selection
|
||||||
def recordEditMode(self):
|
def recordEditMode(self):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
self.addUndo(FileDiffViewer.EditModeUndo(
|
self.addUndo(FileDiffViewerBase.EditModeUndo(
|
||||||
self.mode,
|
self.mode,
|
||||||
self.current_pane,
|
self.current_pane,
|
||||||
self.current_line,
|
self.current_line,
|
||||||
|
@ -1606,7 +1612,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 +1776,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 +1809,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 +1838,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 +1848,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 +2000,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 +2424,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
|
||||||
|
@ -3240,7 +3246,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
# swap the contents of two panes
|
# swap the contents of two panes
|
||||||
def swapPanes(self, f_dst, f_src):
|
def swapPanes(self, f_dst, f_src):
|
||||||
if self.undoblock is not None:
|
if self.undoblock is not None:
|
||||||
self.addUndo(FileDiffViewer.SwapPanesUndo(f_dst, f_src))
|
self.addUndo(FileDiffViewerBase.SwapPanesUndo(f_dst, f_src))
|
||||||
self.current_pane = f_dst
|
self.current_pane = f_dst
|
||||||
f0 = self.panes[f_dst]
|
f0 = self.panes[f_dst]
|
||||||
f1 = self.panes[f_src]
|
f1 = self.panes[f_src]
|
||||||
|
@ -3632,8 +3638,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 +3692,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 +3714,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 +3737,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 +3751,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 +3832,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 +3888,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 +3962,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 +3991,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 +4013,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 +4023,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 +4044,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 +4062,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 +4098,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 +4118,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
|
|
||||||
# pylint: disable=line-too-long
|
# create 'title_changed' signal for FileDiffViewerBase
|
||||||
GObject.signal_new('swapped-panes', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int))
|
GObject.signal_new('swapped-panes', FileDiffViewerBase, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int)) # noqa: E501
|
||||||
GObject.signal_new('num-edits-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, ))
|
GObject.signal_new('num-edits-changed', FileDiffViewerBase, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, )) # noqa: E501
|
||||||
GObject.signal_new('mode-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ())
|
GObject.signal_new('mode-changed', FileDiffViewerBase, 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('cursor-changed', FileDiffViewerBase, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()) # noqa: E501
|
||||||
GObject.signal_new('syntax-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (str, ))
|
GObject.signal_new('syntax-changed', FileDiffViewerBase, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (str, )) # noqa: E501
|
||||||
GObject.signal_new('format-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int))
|
GObject.signal_new('format-changed', FileDiffViewerBase, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int)) # noqa: E501
|
||||||
# pylint: enable=line-too-long
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ def copyFile(src, dest, use_text_mode=False,enc=None):
|
||||||
s = f.read()
|
s = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
if enc is not None:
|
if enc is not None:
|
||||||
s = codecs.encode(unicode(s, 'utf_8'), enc)
|
s = codecs.encode(str(s, encoding='utf_8'), enc)
|
||||||
f = open(dest, w)
|
f = open(dest, w)
|
||||||
f.write(s)
|
f.write(s)
|
||||||
f.close()
|
f.close()
|
||||||
|
@ -190,12 +190,12 @@ for lang in os.listdir(d):
|
||||||
while True:
|
while True:
|
||||||
i = s.find('&#', idx)
|
i = s.find('&#', idx)
|
||||||
if i < 0:
|
if i < 0:
|
||||||
a.append(unicode(s[idx:], 'latin_1'))
|
a.append(str(s[idx:], encoding='latin_1'))
|
||||||
break
|
break
|
||||||
a.append(unicode(s[idx:i], 'latin_1'))
|
a.append(str(s[idx:i], encoding='latin_1'))
|
||||||
i += 2
|
i += 2
|
||||||
j = s.find(';', i)
|
j = s.find(';', i)
|
||||||
a.append(unichr(int(s[i:j])))
|
a.append(chr(int(s[i:j])))
|
||||||
idx = j + 1
|
idx = j + 1
|
||||||
s = ''.join(a)
|
s = ''.join(a)
|
||||||
s = codecs.encode(s, 'utf-8')
|
s = codecs.encode(s, 'utf-8')
|
||||||
|
|
|
@ -196,7 +196,7 @@ def copyFile(src, dest, use_text_mode=False,enc=None):
|
||||||
s = f.read()
|
s = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
if enc is not None:
|
if enc is not None:
|
||||||
s = codecs.encode(unicode(s, 'utf_8'), enc)
|
s = codecs.encode(str(s, encoding='utf_8'), enc)
|
||||||
f = open(dest, w)
|
f = open(dest, w)
|
||||||
f.write(s)
|
f.write(s)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
Loading…
Reference in New Issue