Merge pull request #140 from MightyCreak/static-types
Convert some functions to static typing
This commit is contained in:
commit
8ac41b0b90
|
@ -1,2 +1,3 @@
|
||||||
[mypy]
|
[mypy]
|
||||||
warn_unused_ignores = True
|
warn_unused_ignores = True
|
||||||
|
disallow_incomplete_defs = True
|
||||||
|
|
|
@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Renamed application name from "Diffuse Merge Tool" to "Diffuse"
|
- Renamed application name from "Diffuse Merge Tool" to "Diffuse"
|
||||||
- Linters can be run sooner (before installation)
|
- Linters can be run sooner (before installation)
|
||||||
- Better messages when an error occurs while parsing the config file
|
- Better messages when an error occurs while parsing the config file
|
||||||
|
- Start converting the code to static types
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Removed the lasting lint errors (i.e. in main.py)
|
- Removed the lasting lint errors (i.e. in main.py)
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
APP_NAME = 'Diffuse'
|
APP_NAME: Final[str] = 'Diffuse'
|
||||||
COPYRIGHT = '''{copyright} © 2006-2019 Derrick Moser
|
COPYRIGHT: Final[str] = '''{copyright} © 2006-2019 Derrick Moser
|
||||||
{copyright} © 2015-2021 Romain Failliot'''.format(copyright=_("Copyright"))
|
{copyright} © 2015-2021 Romain Failliot'''.format(copyright=_("Copyright"))
|
||||||
WEBSITE = 'https://mightycreak.github.io/diffuse/'
|
WEBSITE: Final[str] = 'https://mightycreak.github.io/diffuse/'
|
||||||
|
|
||||||
# Constants are set in main()
|
# Constants are set in main()
|
||||||
VERSION = '0.0.0'
|
VERSION: str = '0.0.0'
|
||||||
|
|
|
@ -32,7 +32,7 @@ from gi.repository import GObject, Gtk # type: ignore # noqa: E402
|
||||||
|
|
||||||
# the about dialog
|
# the about dialog
|
||||||
class AboutDialog(Gtk.AboutDialog):
|
class AboutDialog(Gtk.AboutDialog):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Gtk.AboutDialog.__init__(self)
|
Gtk.AboutDialog.__init__(self)
|
||||||
self.set_logo_icon_name('io.github.mightycreak.Diffuse')
|
self.set_logo_icon_name('io.github.mightycreak.Diffuse')
|
||||||
self.set_program_name(constants.APP_NAME)
|
self.set_program_name(constants.APP_NAME)
|
||||||
|
@ -104,13 +104,13 @@ class FileChooserDialog(Gtk.FileChooserDialog):
|
||||||
def set_encoding(self, encoding):
|
def set_encoding(self, encoding):
|
||||||
self.encoding.set_text(encoding)
|
self.encoding.set_text(encoding)
|
||||||
|
|
||||||
def get_encoding(self):
|
def get_encoding(self) -> str:
|
||||||
return self.encoding.get_text()
|
return self.encoding.get_text()
|
||||||
|
|
||||||
def get_revision(self):
|
def get_revision(self) -> str:
|
||||||
return self.revision.get_text()
|
return self.revision.get_text()
|
||||||
|
|
||||||
def get_filename(self):
|
def get_filename(self) -> str:
|
||||||
# convert from UTF-8 string to unicode
|
# convert from UTF-8 string to unicode
|
||||||
return Gtk.FileChooserDialog.get_filename(self)
|
return Gtk.FileChooserDialog.get_filename(self)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import stat
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from typing import Optional
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from diffuse import constants
|
from diffuse import constants
|
||||||
|
@ -33,6 +34,7 @@ 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
|
||||||
from diffuse.resources import theResources
|
from diffuse.resources import theResources
|
||||||
|
from diffuse.utils import LineEnding
|
||||||
from diffuse.vcs.vcs_registry import VcsRegistry
|
from diffuse.vcs.vcs_registry import VcsRegistry
|
||||||
from diffuse.widgets import FileDiffViewerBase
|
from diffuse.widgets import FileDiffViewerBase
|
||||||
from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE
|
from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE
|
||||||
|
@ -118,7 +120,7 @@ class Diffuse(Gtk.Window):
|
||||||
class FileDiffViewer(FileDiffViewerBase):
|
class FileDiffViewer(FileDiffViewerBase):
|
||||||
# pane header
|
# pane header
|
||||||
class PaneHeader(Gtk.Box):
|
class PaneHeader(Gtk.Box):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
||||||
_append_buttons(self, Gtk.IconSize.MENU, [
|
_append_buttons(self, Gtk.IconSize.MENU, [
|
||||||
[Gtk.STOCK_OPEN, self.button_cb, 'open', _('Open File...')],
|
[Gtk.STOCK_OPEN, self.button_cb, 'open', _('Open File...')],
|
||||||
|
@ -171,7 +173,7 @@ class Diffuse(Gtk.Window):
|
||||||
|
|
||||||
# pane footer
|
# pane footer
|
||||||
class PaneFooter(Gtk.Box):
|
class PaneFooter(Gtk.Box):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
||||||
self.cursor = label = Gtk.Label.new()
|
self.cursor = label = Gtk.Label.new()
|
||||||
self.cursor.set_size_request(-1, -1)
|
self.cursor.set_size_request(-1, -1)
|
||||||
|
@ -212,11 +214,11 @@ class Diffuse(Gtk.Window):
|
||||||
# set the format label
|
# set the format label
|
||||||
def setFormat(self, s):
|
def setFormat(self, s):
|
||||||
v = []
|
v = []
|
||||||
if s & utils.DOS_FORMAT:
|
if s & LineEnding.DOS_FORMAT:
|
||||||
v.append('DOS')
|
v.append('DOS')
|
||||||
if s & utils.MAC_FORMAT:
|
if s & LineEnding.MAC_FORMAT:
|
||||||
v.append('Mac')
|
v.append('Mac')
|
||||||
if s & utils.UNIX_FORMAT:
|
if s & LineEnding.UNIX_FORMAT:
|
||||||
v.append('Unix')
|
v.append('Unix')
|
||||||
self.format.set_text('/'.join(v))
|
self.format.set_text('/'.join(v))
|
||||||
|
|
||||||
|
@ -1108,7 +1110,7 @@ class Diffuse(Gtk.Window):
|
||||||
self.quit_cb(widget, data)
|
self.quit_cb(widget, data)
|
||||||
|
|
||||||
# convenience method to request confirmation when closing the last tab
|
# convenience method to request confirmation when closing the last tab
|
||||||
def _confirm_tab_close(self):
|
def _confirm_tab_close(self) -> bool:
|
||||||
dialog = utils.MessageDialog(
|
dialog = utils.MessageDialog(
|
||||||
self.get_toplevel(),
|
self.get_toplevel(),
|
||||||
Gtk.MessageType.WARNING,
|
Gtk.MessageType.WARNING,
|
||||||
|
@ -1193,7 +1195,7 @@ class Diffuse(Gtk.Window):
|
||||||
self.setSyntax(s)
|
self.setSyntax(s)
|
||||||
|
|
||||||
# create an empty viewer with 'n' panes
|
# create an empty viewer with 'n' panes
|
||||||
def newFileDiffViewer(self, n):
|
def newFileDiffViewer(self, n: int) -> FileDiffViewer:
|
||||||
self.viewer_count += 1
|
self.viewer_count += 1
|
||||||
tabname = _('File Merge %d') % (self.viewer_count, )
|
tabname = _('File Merge %d') % (self.viewer_count, )
|
||||||
tab = NotebookTab(tabname, Gtk.STOCK_FILE)
|
tab = NotebookTab(tabname, Gtk.STOCK_FILE)
|
||||||
|
@ -1338,13 +1340,13 @@ class Diffuse(Gtk.Window):
|
||||||
)
|
)
|
||||||
|
|
||||||
# close all tabs without differences
|
# close all tabs without differences
|
||||||
def closeOnSame(self):
|
def closeOnSame(self) -> None:
|
||||||
for i in range(self.notebook.get_n_pages() - 1, -1, -1):
|
for i in range(self.notebook.get_n_pages() - 1, -1, -1):
|
||||||
if not self.notebook.get_nth_page(i).hasDifferences():
|
if not self.notebook.get_nth_page(i).hasDifferences():
|
||||||
self.notebook.remove_page(i)
|
self.notebook.remove_page(i)
|
||||||
|
|
||||||
# returns True if the application can safely quit
|
# returns True if the application can safely quit
|
||||||
def confirmQuit(self):
|
def confirmQuit(self) -> bool:
|
||||||
nb = self.notebook
|
nb = self.notebook
|
||||||
return self.confirmCloseViewers([nb.get_nth_page(i) for i in range(nb.get_n_pages())])
|
return self.confirmCloseViewers([nb.get_nth_page(i) for i in range(nb.get_n_pages())])
|
||||||
|
|
||||||
|
@ -1356,7 +1358,7 @@ class Diffuse(Gtk.Window):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# returns the currently focused viewer
|
# returns the currently focused viewer
|
||||||
def getCurrentViewer(self):
|
def getCurrentViewer(self) -> Optional[Gtk.Widget]:
|
||||||
return self.notebook.get_nth_page(self.notebook.get_current_page())
|
return self.notebook.get_nth_page(self.notebook.get_current_page())
|
||||||
|
|
||||||
# callback for the open file menu item
|
# callback for the open file menu item
|
||||||
|
@ -1570,7 +1572,7 @@ class Diffuse(Gtk.Window):
|
||||||
self.getCurrentViewer().go_to_line_cb(widget, data)
|
self.getCurrentViewer().go_to_line_cb(widget, data)
|
||||||
|
|
||||||
# notify all viewers of changes to the preferences
|
# notify all viewers of changes to the preferences
|
||||||
def preferences_updated(self):
|
def preferences_updated(self) -> None:
|
||||||
n = self.notebook.get_n_pages()
|
n = self.notebook.get_n_pages()
|
||||||
self.notebook.set_show_tabs(self.prefs.getBool('tabs_always_show') or n > 1)
|
self.notebook.set_show_tabs(self.prefs.getBool('tabs_always_show') or n > 1)
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
|
@ -1712,7 +1714,7 @@ def _append_buttons(box, size, specs):
|
||||||
|
|
||||||
|
|
||||||
# constructs a full URL for the named file
|
# constructs a full URL for the named file
|
||||||
def _path2url(path, proto='file'):
|
def _path2url(path: str, proto: str = 'file') -> str:
|
||||||
r = [proto, ':///']
|
r = [proto, ':///']
|
||||||
s = os.path.abspath(path)
|
s = os.path.abspath(path)
|
||||||
i = 0
|
i = 0
|
||||||
|
|
|
@ -199,7 +199,7 @@ class Preferences:
|
||||||
self.path = path
|
self.path = path
|
||||||
if os.path.isfile(self.path):
|
if os.path.isfile(self.path):
|
||||||
try:
|
try:
|
||||||
with open(self.path, 'r', encoding='utf-8') as f:
|
with open(self.path, 'r', encoding='utf-8') as f:
|
||||||
ss = utils.readconfiglines(f)
|
ss = utils.readconfiglines(f)
|
||||||
for j, s in enumerate(ss):
|
for j, s in enumerate(ss):
|
||||||
try:
|
try:
|
||||||
|
@ -342,7 +342,7 @@ class Preferences:
|
||||||
if tpl[0] in ['Font', 'Integer']:
|
if tpl[0] in ['Font', 'Integer']:
|
||||||
entry = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
entry = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
||||||
if tpl[0] == 'Font':
|
if tpl[0] == 'Font':
|
||||||
button = _FontButton()
|
button = Gtk.FontButton()
|
||||||
button.set_font(self.string_prefs[tpl[1]])
|
button.set_font(self.string_prefs[tpl[1]])
|
||||||
else:
|
else:
|
||||||
button = Gtk.SpinButton.new(
|
button = Gtk.SpinButton.new(
|
||||||
|
@ -368,28 +368,28 @@ class Preferences:
|
||||||
return table
|
return table
|
||||||
|
|
||||||
# get/set methods to manipulate the preference values
|
# get/set methods to manipulate the preference values
|
||||||
def getBool(self, name):
|
def getBool(self, name: str) -> bool:
|
||||||
return self.bool_prefs[name]
|
return self.bool_prefs[name]
|
||||||
|
|
||||||
def setBool(self, name, value):
|
def setBool(self, name: str, value: bool) -> None:
|
||||||
self.bool_prefs[name] = value
|
self.bool_prefs[name] = value
|
||||||
|
|
||||||
def getInt(self, name):
|
def getInt(self, name: str) -> int:
|
||||||
return self.int_prefs[name]
|
return self.int_prefs[name]
|
||||||
|
|
||||||
def getString(self, name):
|
def getString(self, name: str) -> str:
|
||||||
return self.string_prefs[name]
|
return self.string_prefs[name]
|
||||||
|
|
||||||
def setString(self, name, value):
|
def setString(self, name: str, value: str) -> None:
|
||||||
self.string_prefs[name] = value
|
self.string_prefs[name] = value
|
||||||
|
|
||||||
def getEncodings(self):
|
def getEncodings(self):
|
||||||
return self.encodings
|
return self.encodings
|
||||||
|
|
||||||
def _getDefaultEncodings(self):
|
def _getDefaultEncodings(self) -> list[str]:
|
||||||
return self.string_prefs['encoding_auto_detect_codecs'].split()
|
return self.string_prefs['encoding_auto_detect_codecs'].split()
|
||||||
|
|
||||||
def getDefaultEncoding(self):
|
def getDefaultEncoding(self) -> str:
|
||||||
return self.string_prefs['encoding_default_codec']
|
return self.string_prefs['encoding_default_codec']
|
||||||
|
|
||||||
# attempt to convert a string to unicode from an unknown encoding
|
# attempt to convert a string to unicode from an unknown encoding
|
||||||
|
@ -412,7 +412,7 @@ class Preferences:
|
||||||
|
|
||||||
# cygwin and native applications can be used on windows, use this method
|
# cygwin and native applications can be used on windows, use this method
|
||||||
# to convert a path to the usual form expected on sys.platform
|
# to convert a path to the usual form expected on sys.platform
|
||||||
def convertToNativePath(self, s):
|
def convertToNativePath(self, s: str) -> str:
|
||||||
if utils.isWindows() and s.find('/') >= 0:
|
if utils.isWindows() and s.find('/') >= 0:
|
||||||
# treat as a cygwin path
|
# treat as a cygwin path
|
||||||
s = s.replace(os.sep, '/')
|
s = s.replace(os.sep, '/')
|
||||||
|
@ -436,15 +436,6 @@ class Preferences:
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
# adaptor class to allow a Gtk.FontButton to be read like a Gtk.Entry
|
|
||||||
class _FontButton(Gtk.FontButton):
|
|
||||||
def __init__(self):
|
|
||||||
Gtk.FontButton.__init__(self)
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
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):
|
||||||
|
@ -475,8 +466,8 @@ class _FileEntry(Gtk.Box):
|
||||||
self.entry.set_text(dialog.get_filename())
|
self.entry.set_text(dialog.get_filename())
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
def set_text(self, s):
|
def set_text(self, s: str) -> None:
|
||||||
self.entry.set_text(s)
|
self.entry.set_text(s)
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self) -> str:
|
||||||
return self.entry.get_text()
|
return self.entry.get_text()
|
||||||
|
|
|
@ -31,6 +31,7 @@ import shlex
|
||||||
|
|
||||||
from distutils import util
|
from distutils import util
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
|
|
||||||
|
@ -529,7 +530,7 @@ class Resources:
|
||||||
|
|
||||||
# colour resources
|
# colour resources
|
||||||
class _Colour:
|
class _Colour:
|
||||||
def __init__(self, r, g, b, a=1.0):
|
def __init__(self, r: float, g: float, b: float, a: float = 1.0):
|
||||||
# the individual colour components as floats in the range [0, 1]
|
# the individual colour components as floats in the range [0, 1]
|
||||||
self.red = r
|
self.red = r
|
||||||
self.green = g
|
self.green = g
|
||||||
|
@ -601,4 +602,4 @@ class _SyntaxParser:
|
||||||
return state_name, blocks
|
return state_name, blocks
|
||||||
|
|
||||||
|
|
||||||
theResources = Resources()
|
theResources: Final[Resources] = Resources()
|
||||||
|
|
|
@ -23,7 +23,9 @@ import locale
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from enum import IntFlag
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from typing import Final, Optional, TextIO
|
||||||
|
|
||||||
from diffuse import constants
|
from diffuse import constants
|
||||||
from diffuse.resources import theResources
|
from diffuse.resources import theResources
|
||||||
|
@ -74,38 +76,38 @@ class EncodingMenu(Gtk.Box):
|
||||||
return self.encodings[i] if i >= 0 else None
|
return self.encodings[i] if i >= 0 else None
|
||||||
|
|
||||||
|
|
||||||
# platform test
|
def isWindows() -> bool:
|
||||||
def isWindows():
|
'''Returns true if OS is Windows; otherwise false.'''
|
||||||
return os.name == 'nt'
|
return os.name == 'nt'
|
||||||
|
|
||||||
|
|
||||||
def _logPrintOutput(msg):
|
def _logPrintOutput(msg: str) -> None:
|
||||||
if theResources.getOptionAsBool('log_print_output'):
|
if theResources.getOptionAsBool('log_print_output'):
|
||||||
print(msg, file=sys.stderr)
|
print(msg, file=sys.stderr)
|
||||||
if theResources.getOptionAsBool('log_print_stack'):
|
if theResources.getOptionAsBool('log_print_stack'):
|
||||||
traceback.print_stack()
|
traceback.print_stack()
|
||||||
|
|
||||||
|
|
||||||
# convenience function to display debug messages
|
def logDebug(msg: str) -> None:
|
||||||
def logDebug(msg):
|
'''Report debug message.'''
|
||||||
_logPrintOutput(f'DEBUG: {msg}')
|
_logPrintOutput(f'DEBUG: {msg}')
|
||||||
|
|
||||||
|
|
||||||
# report error messages
|
def logError(msg: str) -> None:
|
||||||
def logError(msg):
|
'''Report error message.'''
|
||||||
_logPrintOutput(f'ERROR: {msg}')
|
_logPrintOutput(f'ERROR: {msg}')
|
||||||
|
|
||||||
|
|
||||||
# report error messages and show dialog
|
def logErrorAndDialog(msg: str, parent: Gtk.Widget = None) -> None:
|
||||||
def logErrorAndDialog(msg, parent=None):
|
'''Report error message and show dialog.'''
|
||||||
logError(msg)
|
logError(msg)
|
||||||
dialog = MessageDialog(parent, Gtk.MessageType.ERROR, msg)
|
dialog = MessageDialog(parent, Gtk.MessageType.ERROR, msg)
|
||||||
dialog.run()
|
dialog.run()
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
|
|
||||||
# create nested subdirectories and return the complete path
|
def make_subdirs(p: str, ss: list[str]) -> str:
|
||||||
def make_subdirs(p, ss):
|
'''Create nested subdirectories and return the complete path.'''
|
||||||
for s in ss:
|
for s in ss:
|
||||||
p = os.path.join(p, s)
|
p = os.path.join(p, s)
|
||||||
if not os.path.exists(p):
|
if not os.path.exists(p):
|
||||||
|
@ -116,16 +118,16 @@ def make_subdirs(p, ss):
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
# returns the Windows drive or share from a from an absolute path
|
def _drive_from_path(path: str) -> str:
|
||||||
def _drive_from_path(path):
|
'''Returns the Windows drive or share from a from an absolute path.'''
|
||||||
d = path.split(os.sep)
|
d = path.split(os.sep)
|
||||||
if len(d) > 3 and d[0] == '' and d[1] == '':
|
if len(d) > 3 and d[0] == '' and d[1] == '':
|
||||||
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
|
def relpath(a: str, b: str) -> str:
|
||||||
def relpath(a, b):
|
'''Constructs a relative path from 'a' to 'b', both should be absolute paths.'''
|
||||||
if isWindows():
|
if isWindows():
|
||||||
if _drive_from_path(a) != _drive_from_path(b):
|
if _drive_from_path(a) != _drive_from_path(b):
|
||||||
return b
|
return b
|
||||||
|
@ -151,12 +153,12 @@ def safeRelativePath(abspath1, name, prefs, cygwin_pref):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
# escape arguments for use with bash
|
def _bash_escape(s: str) -> str:
|
||||||
def _bash_escape(s):
|
'''Escape arguments for use with bash.'''
|
||||||
return "'" + s.replace("'", "'\\''") + "'"
|
return "'" + s.replace("'", "'\\''") + "'"
|
||||||
|
|
||||||
|
|
||||||
def _use_flatpak():
|
def _use_flatpak() -> bool:
|
||||||
return theResources.getOptionAsBool('use_flatpak')
|
return theResources.getOptionAsBool('use_flatpak')
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,9 +203,8 @@ def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
# returns the number of characters in the string excluding any line ending
|
def len_minus_line_ending(s: str) -> int:
|
||||||
# characters
|
'''Returns the number of characters in the string excluding any line ending characters.'''
|
||||||
def len_minus_line_ending(s):
|
|
||||||
if s is None:
|
if s is None:
|
||||||
return 0
|
return 0
|
||||||
n = len(s)
|
n = len(s)
|
||||||
|
@ -214,15 +215,15 @@ def len_minus_line_ending(s):
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
# returns the string without the line ending characters
|
def strip_eol(s: str) -> str:
|
||||||
def strip_eol(s):
|
'''Returns the string without the line ending characters.'''
|
||||||
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
|
def _strip_eols(ss: list[str]) -> list[str]:
|
||||||
def _strip_eols(ss):
|
'''Returns the list of strings without line ending characters.'''
|
||||||
return [strip_eol(s) for s in ss]
|
return [strip_eol(s) for s in ss]
|
||||||
|
|
||||||
|
|
||||||
|
@ -232,12 +233,12 @@ def popenReadLines(dn, cmd, prefs, bash_pref, success_results=None):
|
||||||
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: TextIO) -> list[str]:
|
||||||
return fd.read().replace('\r', '').split('\n')
|
return fd.read().replace('\r', '').split('\n')
|
||||||
|
|
||||||
|
|
||||||
# escape special glob characters
|
def globEscape(s: str) -> str:
|
||||||
def globEscape(s):
|
'''Escape special glob characters.'''
|
||||||
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])
|
||||||
|
|
||||||
|
@ -272,25 +273,25 @@ def splitlines(text: str) -> list[str]:
|
||||||
|
|
||||||
|
|
||||||
# also recognize old Mac OS line endings
|
# also recognize old Mac OS line endings
|
||||||
def readlines(fd):
|
def readlines(fd: TextIO) -> list[str]:
|
||||||
return _strip_eols(splitlines(fd.read()))
|
return _strip_eols(splitlines(fd.read()))
|
||||||
|
|
||||||
|
|
||||||
# map an encoding name to its standard form
|
def norm_encoding(e: Optional[str]) -> Optional[str]:
|
||||||
def norm_encoding(e):
|
'''Map an encoding name to its standard form.'''
|
||||||
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: Optional[str]) -> str:
|
||||||
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: Gtk.Adjustment, delta: int) -> None:
|
||||||
v = adj.get_value() + delta
|
v = adj.get_value() + delta
|
||||||
# clamp to the allowed range
|
# clamp to the allowed range
|
||||||
v = max(v, int(adj.get_lower()))
|
v = max(v, int(adj.get_lower()))
|
||||||
|
@ -298,36 +299,43 @@ def step_adjustment(adj, delta):
|
||||||
adj.set_value(v)
|
adj.set_value(v)
|
||||||
|
|
||||||
|
|
||||||
# masks used to indicate the presence of particular line endings
|
def _get_default_lang() -> Optional[str]:
|
||||||
DOS_FORMAT = 1
|
lang = locale.getdefaultlocale()[0]
|
||||||
MAC_FORMAT = 2
|
if isWindows():
|
||||||
UNIX_FORMAT = 4
|
# gettext looks for the language using environment variables which
|
||||||
|
# are normally not set on Windows so we try setting it for them
|
||||||
|
for lang_env in 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE':
|
||||||
|
if lang_env in os.environ:
|
||||||
|
lang = os.environ[lang_env]
|
||||||
|
# remove any additional languages, encodings, or modifications
|
||||||
|
for c in ':.@':
|
||||||
|
lang = lang.split(c)[0]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if lang is not None:
|
||||||
|
os.environ['LANG'] = lang
|
||||||
|
return lang
|
||||||
|
|
||||||
|
|
||||||
|
class LineEnding(IntFlag):
|
||||||
|
'''Enumeration of line endings.
|
||||||
|
|
||||||
|
Values can be used as flags in bitwise operations.'''
|
||||||
|
|
||||||
|
DOS_FORMAT = 1
|
||||||
|
MAC_FORMAT = 2
|
||||||
|
UNIX_FORMAT = 4
|
||||||
|
|
||||||
|
|
||||||
# avoid some dictionary lookups when string.whitespace is used in loops
|
# avoid some dictionary lookups when string.whitespace is used in loops
|
||||||
# this is sorted based upon frequency to speed up code for stripping whitespace
|
# this is sorted based upon frequency to speed up code for stripping whitespace
|
||||||
whitespace = ' \t\n\r\x0b\x0c'
|
whitespace: Final[str] = ' \t\n\r\x0b\x0c'
|
||||||
|
|
||||||
# use the program's location as a starting place to search for supporting files
|
# use the program's location as a starting place to search for supporting files
|
||||||
# such as icon and help documentation
|
# such as icon and help documentation
|
||||||
if hasattr(sys, 'frozen'):
|
app_path: Final[str] = sys.executable if hasattr(sys, 'frozen') else os.path.realpath(sys.argv[0])
|
||||||
app_path = sys.executable
|
bin_dir: Final[str] = os.path.dirname(app_path)
|
||||||
else:
|
|
||||||
app_path = os.path.realpath(sys.argv[0])
|
|
||||||
bin_dir = os.path.dirname(app_path)
|
|
||||||
|
|
||||||
# translation location: '../share/locale/<LANG>/LC_MESSAGES/diffuse.mo'
|
# translation location: '../share/locale/<LANG>/LC_MESSAGES/diffuse.mo'
|
||||||
# where '<LANG>' is the language key
|
# where '<LANG>' is the language key
|
||||||
lang = locale.getdefaultlocale()[0]
|
lang: Final[Optional[str]] = _get_default_lang()
|
||||||
if isWindows():
|
|
||||||
# gettext looks for the language using environment variables which
|
|
||||||
# are normally not set on Windows so we try setting it for them
|
|
||||||
for lang_env in 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE':
|
|
||||||
if lang_env in os.environ:
|
|
||||||
lang = os.environ[lang_env]
|
|
||||||
# remove any additional languages, encodings, or modifications
|
|
||||||
for c in ':.@':
|
|
||||||
lang = lang.split(c)[0]
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if lang is not None:
|
|
||||||
os.environ['LANG'] = lang
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
|
from diffuse.preferences import Preferences
|
||||||
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
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ class Git(VcsInterface):
|
||||||
# sort the results
|
# sort the results
|
||||||
return [modified[k] for k in sorted(modified.keys())]
|
return [modified[k] for k in sorted(modified.keys())]
|
||||||
|
|
||||||
def _extractPath(self, s, prefs):
|
def _extractPath(self, s: str, prefs: Preferences) -> str:
|
||||||
return os.path.join(self.root, prefs.convertToNativePath(s.strip()))
|
return os.path.join(self.root, prefs.convertToNativePath(s.strip()))
|
||||||
|
|
||||||
def getFolderTemplate(self, prefs, names):
|
def getFolderTemplate(self, prefs, names):
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from diffuse import utils
|
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
|
||||||
|
@ -26,9 +28,9 @@ 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: str):
|
||||||
VcsInterface.__init__(self, root)
|
VcsInterface.__init__(self, root)
|
||||||
self.working_rev = None
|
self.working_rev: Optional[str] = None
|
||||||
|
|
||||||
def _getPreviousRevision(self, prefs, rev):
|
def _getPreviousRevision(self, prefs, rev):
|
||||||
if rev is None:
|
if rev is None:
|
||||||
|
|
|
@ -25,21 +25,21 @@ from diffuse.vcs.svn import Svn
|
||||||
|
|
||||||
class Svk(Svn):
|
class Svk(Svn):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _getVcs():
|
def _getVcs() -> str:
|
||||||
return 'svk'
|
return 'svk'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _getURLPrefix():
|
def _getURLPrefix() -> str:
|
||||||
return 'Depot Path: '
|
return 'Depot Path: '
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parseStatusLine(s):
|
def _parseStatusLine(s: str) -> tuple[str, str]:
|
||||||
if len(s) < 4 or s[0] not in 'ACDMR':
|
if len(s) < 4 or s[0] not in 'ACDMR':
|
||||||
return '', ''
|
return '', ''
|
||||||
return s[0], s[4:]
|
return s[0], s[4:]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _getPreviousRevision(rev):
|
def _getPreviousRevision(rev: str) -> str:
|
||||||
if rev is None:
|
if rev is None:
|
||||||
return 'HEAD'
|
return 'HEAD'
|
||||||
if rev.endswith('@'):
|
if rev.endswith('@'):
|
||||||
|
|
|
@ -21,6 +21,7 @@ import os
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
from diffuse.vcs.folder_set import FolderSet
|
from diffuse.vcs.folder_set import FolderSet
|
||||||
|
@ -30,20 +31,20 @@ 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):
|
||||||
def __init__(self, root):
|
def __init__(self, root: str):
|
||||||
VcsInterface.__init__(self, root)
|
VcsInterface.__init__(self, root)
|
||||||
self.url = None
|
self.url: Optional[str] = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _getVcs():
|
def _getVcs() -> str:
|
||||||
return 'svn'
|
return 'svn'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _getURLPrefix():
|
def _getURLPrefix() -> str:
|
||||||
return 'URL: '
|
return 'URL: '
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parseStatusLine(s):
|
def _parseStatusLine(s: str) -> tuple[str, str]:
|
||||||
if len(s) < 8 or s[0] not in 'ACDMR':
|
if len(s) < 8 or s[0] not in 'ACDMR':
|
||||||
return '', ''
|
return '', ''
|
||||||
# subversion 1.6 adds a new column
|
# subversion 1.6 adds a new column
|
||||||
|
@ -53,7 +54,7 @@ class Svn(VcsInterface):
|
||||||
return s[0], s[k:]
|
return s[0], s[k:]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _getPreviousRevision(rev):
|
def _getPreviousRevision(rev: str) -> str:
|
||||||
if rev is None:
|
if rev is None:
|
||||||
return 'BASE'
|
return 'BASE'
|
||||||
m = int(rev)
|
m = int(rev)
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
class VcsInterface:
|
class VcsInterface:
|
||||||
"""Interface for the VCSs."""
|
"""Interface for the VCSs."""
|
||||||
|
|
||||||
def __init__(self, root):
|
def __init__(self, root: str):
|
||||||
"""The object will initialized with the repository's root folder."""
|
"""The object will initialized with the repository's root folder."""
|
||||||
self.root = root
|
self.root = root
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,12 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
|
from diffuse.preferences import Preferences
|
||||||
from diffuse.vcs.folder_set import FolderSet
|
from diffuse.vcs.folder_set import FolderSet
|
||||||
|
from diffuse.vcs.vcs_interface import VcsInterface
|
||||||
from diffuse.vcs.bzr import Bzr
|
from diffuse.vcs.bzr import Bzr
|
||||||
from diffuse.vcs.cvs import Cvs
|
from diffuse.vcs.cvs import Cvs
|
||||||
from diffuse.vcs.darcs import Darcs
|
from diffuse.vcs.darcs import Darcs
|
||||||
|
@ -35,7 +38,7 @@ from diffuse.vcs.svn import Svn
|
||||||
|
|
||||||
|
|
||||||
class VcsRegistry:
|
class VcsRegistry:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
# initialise the VCS objects
|
# initialise the VCS objects
|
||||||
self._get_repo = {
|
self._get_repo = {
|
||||||
'bzr': _get_bzr_repo,
|
'bzr': _get_bzr_repo,
|
||||||
|
@ -50,7 +53,7 @@ class VcsRegistry:
|
||||||
}
|
}
|
||||||
|
|
||||||
# determines which VCS to use for files in the named folder
|
# determines which VCS to use for files in the named folder
|
||||||
def findByFolder(self, path, prefs):
|
def findByFolder(self, path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
path = os.path.abspath(path)
|
path = os.path.abspath(path)
|
||||||
for vcs in prefs.getString('vcs_search_order').split():
|
for vcs in prefs.getString('vcs_search_order').split():
|
||||||
if vcs in self._get_repo:
|
if vcs in self._get_repo:
|
||||||
|
@ -60,14 +63,14 @@ class VcsRegistry:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# determines which VCS to use for the named file
|
# determines which VCS to use for the named file
|
||||||
def findByFilename(self, name, prefs):
|
def findByFilename(self, name: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
if name is not None:
|
if name is not None:
|
||||||
return self.findByFolder(os.path.dirname(name), prefs)
|
return self.findByFolder(os.path.dirname(name), prefs)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# utility method to help find folders used by version control systems
|
# utility method to help find folders used by version control systems
|
||||||
def _find_parent_dir_with(path, dir_name):
|
def _find_parent_dir_with(path: str, dir_name: str) -> Optional[str]:
|
||||||
while True:
|
while True:
|
||||||
name = os.path.join(path, dir_name)
|
name = os.path.join(path, dir_name)
|
||||||
if os.path.isdir(name):
|
if os.path.isdir(name):
|
||||||
|
@ -76,28 +79,28 @@ def _find_parent_dir_with(path, dir_name):
|
||||||
if newpath == path:
|
if newpath == path:
|
||||||
break
|
break
|
||||||
path = newpath
|
path = newpath
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _get_bzr_repo(path, prefs):
|
def _get_bzr_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
if 'GIT_DIR' in os.environ:
|
if 'GIT_DIR' in os.environ:
|
||||||
try:
|
try:
|
||||||
d = path
|
ss: list[str] = utils.popenReadLines(
|
||||||
ss = utils.popenReadLines(
|
path,
|
||||||
d,
|
|
||||||
[
|
[
|
||||||
prefs.getString('git_bin'),
|
prefs.getString('git_bin'),
|
||||||
'rev-parse',
|
'rev-parse',
|
||||||
|
@ -107,20 +110,20 @@ def _get_git_repo(path, prefs):
|
||||||
'git_bash')
|
'git_bash')
|
||||||
if len(ss) > 0:
|
if len(ss) > 0:
|
||||||
# be careful to handle trailing slashes
|
# be careful to handle trailing slashes
|
||||||
d = d.split(os.sep)
|
dirs = path.split(os.sep)
|
||||||
if d[-1] != '':
|
if dirs[-1] != '':
|
||||||
d.append('')
|
dirs.append('')
|
||||||
ss = utils.strip_eol(ss[0]).split('/')
|
ss = utils.strip_eol(ss[0]).split('/')
|
||||||
if ss[-1] != '':
|
if ss[-1] != '':
|
||||||
ss.append('')
|
ss.append('')
|
||||||
n = len(ss)
|
n = len(ss)
|
||||||
if n <= len(d):
|
if n <= len(dirs):
|
||||||
del d[-n:]
|
del dirs[-n:]
|
||||||
if len(d) == 0:
|
if len(dirs) == 0:
|
||||||
d = os.curdir
|
path = os.curdir
|
||||||
else:
|
else:
|
||||||
d = os.sep.join(d)
|
path = os.sep.join(dirs)
|
||||||
return Git(d)
|
return Git(path)
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
# working tree not found
|
# working tree not found
|
||||||
pass
|
pass
|
||||||
|
@ -133,19 +136,20 @@ def _get_git_repo(path, prefs):
|
||||||
if newpath == path:
|
if newpath == path:
|
||||||
break
|
break
|
||||||
path = newpath
|
path = newpath
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _get_hg_repo(path, prefs):
|
def _get_hg_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
if os.path.isdir(os.path.join(path, 'RCS')):
|
if os.path.isdir(os.path.join(path, 'RCS')):
|
||||||
return Rcs(path)
|
return Rcs(path)
|
||||||
|
|
||||||
|
@ -162,12 +166,12 @@ def _get_rcs_repo(path, prefs):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _get_svn_repo(path, prefs):
|
def _get_svn_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||||
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
|
||||||
# SVK repositories
|
# SVK repositories
|
||||||
|
@ -181,7 +185,7 @@ def _get_svk_repo(path, prefs):
|
||||||
try:
|
try:
|
||||||
# find working copies by parsing the config file
|
# find working copies by parsing the config file
|
||||||
with open(svkconfig, 'r', encoding='utf-8') as f:
|
with open(svkconfig, 'r', encoding='utf-8') as f:
|
||||||
ss = utils.readlines(f)
|
ss: list[str] = utils.readlines(f)
|
||||||
projs, sep = [], os.sep
|
projs, sep = [], os.sep
|
||||||
# find the separator character
|
# find the separator character
|
||||||
for s in ss:
|
for s in ss:
|
||||||
|
|
|
@ -22,10 +22,11 @@ import os
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from typing import Dict
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from diffuse import utils
|
from diffuse import utils
|
||||||
from diffuse.resources import theResources
|
from diffuse.resources import theResources
|
||||||
|
from diffuse.utils import LineEnding
|
||||||
|
|
||||||
import gi # type: ignore
|
import gi # type: ignore
|
||||||
gi.require_version('GObject', '2.0')
|
gi.require_version('GObject', '2.0')
|
||||||
|
@ -148,9 +149,9 @@ class ScrolledWindow(Gtk.Grid):
|
||||||
class FileDiffViewerBase(Gtk.Grid):
|
class FileDiffViewerBase(Gtk.Grid):
|
||||||
# class describing a text pane
|
# class describing a text pane
|
||||||
class Pane:
|
class Pane:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
# list of lines displayed in this pane (including spacing lines)
|
# list of lines displayed in this pane (including spacing lines)
|
||||||
self.lines = []
|
self.lines: List[FileDiffViewerBase.Line] = []
|
||||||
# high water mark for line length in Pango units (used to determine
|
# high water mark for line length in Pango units (used to determine
|
||||||
# the required horizontal scroll range)
|
# the required horizontal scroll range)
|
||||||
self.line_lengths = 0
|
self.line_lengths = 0
|
||||||
|
@ -160,11 +161,11 @@ class FileDiffViewerBase(Gtk.Grid):
|
||||||
# self.syntax_cache[i] corresponds to self.lines[i]
|
# self.syntax_cache[i] corresponds to self.lines[i]
|
||||||
# the list is truncated when a change to a line invalidates a
|
# the list is truncated when a change to a line invalidates a
|
||||||
# portion of the cache
|
# portion of the cache
|
||||||
self.syntax_cache = []
|
self.syntax_cache: List[List[Any]] = []
|
||||||
# cache of character differences for each line
|
# cache of character differences for each line
|
||||||
# self.diff_cache[i] corresponds to self.lines[i]
|
# self.diff_cache[i] corresponds to self.lines[i]
|
||||||
# portion of the cache are cleared by setting entries to None
|
# portion of the cache are cleared by setting entries to None
|
||||||
self.diff_cache = []
|
self.diff_cache: List[List[Any]] = []
|
||||||
# mask indicating the type of line endings present
|
# mask indicating the type of line endings present
|
||||||
self.format = 0
|
self.format = 0
|
||||||
# number of lines with edits
|
# number of lines with edits
|
||||||
|
@ -314,7 +315,7 @@ class FileDiffViewerBase(Gtk.Grid):
|
||||||
|
|
||||||
# create panes
|
# create panes
|
||||||
self.dareas = []
|
self.dareas = []
|
||||||
self.panes = []
|
self.panes: List[FileDiffViewerBase.Pane] = []
|
||||||
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):
|
||||||
|
@ -3539,15 +3540,15 @@ class FileDiffViewerBase(Gtk.Grid):
|
||||||
|
|
||||||
# 'convert_to_dos' action
|
# 'convert_to_dos' action
|
||||||
def convert_to_dos(self):
|
def convert_to_dos(self):
|
||||||
self.convert_format(utils.DOS_FORMAT)
|
self.convert_format(LineEnding.DOS_FORMAT)
|
||||||
|
|
||||||
# 'convert_to_mac' action
|
# 'convert_to_mac' action
|
||||||
def convert_to_mac(self):
|
def convert_to_mac(self):
|
||||||
self.convert_format(utils.MAC_FORMAT)
|
self.convert_format(LineEnding.MAC_FORMAT)
|
||||||
|
|
||||||
# 'convert_to_unix' action
|
# 'convert_to_unix' action
|
||||||
def convert_to_unix(self):
|
def convert_to_unix(self):
|
||||||
self.convert_format(utils.UNIX_FORMAT)
|
self.convert_format(LineEnding.UNIX_FORMAT)
|
||||||
|
|
||||||
# copies the selected range of lines from pane 'f_src' to 'f_dst'
|
# copies the selected range of lines from pane 'f_src' to 'f_dst'
|
||||||
def merge_lines(self, f_dst, f_src):
|
def merge_lines(self, f_dst, f_src):
|
||||||
|
@ -3985,11 +3986,11 @@ def _get_format(ss):
|
||||||
for s in ss:
|
for s in ss:
|
||||||
if s is not None:
|
if s is not None:
|
||||||
if _has_dos_line_ending(s):
|
if _has_dos_line_ending(s):
|
||||||
flags |= utils.DOS_FORMAT
|
flags |= LineEnding.DOS_FORMAT
|
||||||
elif _has_mac_line_ending(s):
|
elif _has_mac_line_ending(s):
|
||||||
flags |= utils.MAC_FORMAT
|
flags |= LineEnding.MAC_FORMAT
|
||||||
elif _has_unix_line_ending(s):
|
elif _has_unix_line_ending(s):
|
||||||
flags |= utils.UNIX_FORMAT
|
flags |= LineEnding.UNIX_FORMAT
|
||||||
return flags
|
return flags
|
||||||
|
|
||||||
|
|
||||||
|
@ -4000,17 +4001,17 @@ def _convert_to_format(s, fmt):
|
||||||
if old_format != 0 and (old_format & fmt) == 0:
|
if old_format != 0 and (old_format & fmt) == 0:
|
||||||
s = utils.strip_eol(s)
|
s = utils.strip_eol(s)
|
||||||
# prefer the host line ending style
|
# prefer the host line ending style
|
||||||
if (fmt & utils.DOS_FORMAT) and os.linesep == '\r\n':
|
if (fmt & LineEnding.DOS_FORMAT) and os.linesep == '\r\n':
|
||||||
s += os.linesep
|
s += os.linesep
|
||||||
elif (fmt & utils.MAC_FORMAT) and os.linesep == '\r':
|
elif (fmt & LineEnding.MAC_FORMAT) and os.linesep == '\r':
|
||||||
s += os.linesep
|
s += os.linesep
|
||||||
elif (fmt & utils.UNIX_FORMAT) and os.linesep == '\n':
|
elif (fmt & LineEnding.UNIX_FORMAT) and os.linesep == '\n':
|
||||||
s += os.linesep
|
s += os.linesep
|
||||||
elif fmt & utils.UNIX_FORMAT:
|
elif fmt & LineEnding.UNIX_FORMAT:
|
||||||
s += '\n'
|
s += '\n'
|
||||||
elif fmt & utils.DOS_FORMAT:
|
elif fmt & LineEnding.DOS_FORMAT:
|
||||||
s += '\r\n'
|
s += '\r\n'
|
||||||
elif fmt & utils.MAC_FORMAT:
|
elif fmt & LineEnding.MAC_FORMAT:
|
||||||
s += '\r'
|
s += '\r'
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue