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