Merge pull request #142 from MightyCreak/more-static-typing
Convert more code to static typing
This commit is contained in:
commit
2231b79fc8
|
@ -26,7 +26,7 @@ import stat
|
|||
import webbrowser
|
||||
|
||||
from gettext import gettext as _
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from diffuse import constants
|
||||
|
@ -35,9 +35,10 @@ from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, Searc
|
|||
from diffuse.preferences import Preferences
|
||||
from diffuse.resources import theResources
|
||||
from diffuse.utils import LineEnding
|
||||
from diffuse.vcs.vcs_interface import VcsInterface
|
||||
from diffuse.vcs.vcs_registry import VcsRegistry
|
||||
from diffuse.widgets import FileDiffViewerBase
|
||||
from diffuse.widgets import createMenu, LINE_MODE, CHAR_MODE, ALIGN_MODE
|
||||
from diffuse.widgets import FileDiffViewerBase, EditMode
|
||||
from diffuse.widgets import createMenu
|
||||
|
||||
import gi # type: ignore
|
||||
gi.require_version('GObject', '2.0')
|
||||
|
@ -57,7 +58,7 @@ theVCSs = VcsRegistry()
|
|||
# make this a Gtk.EventBox so signals can be connected for MMB and RMB button
|
||||
# presses.
|
||||
class NotebookTab(Gtk.EventBox):
|
||||
def __init__(self, name, stock):
|
||||
def __init__(self, name: str, stock: str) -> None:
|
||||
Gtk.EventBox.__init__(self)
|
||||
self.set_visible_window(False)
|
||||
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
||||
|
@ -66,13 +67,16 @@ class NotebookTab(Gtk.EventBox):
|
|||
image.set_from_stock(stock, Gtk.IconSize.MENU)
|
||||
hbox.pack_start(image, False, False, 5)
|
||||
image.show()
|
||||
self.label = label = Gtk.Label.new(name)
|
||||
|
||||
label = Gtk.Label.new(name)
|
||||
# left justify the widget
|
||||
label.set_xalign(0.0)
|
||||
label.set_yalign(0.5)
|
||||
hbox.pack_start(label, True, True, 0)
|
||||
label.show()
|
||||
self.button = button = Gtk.Button.new()
|
||||
self.label = label
|
||||
|
||||
button = Gtk.Button.new()
|
||||
button.set_relief(Gtk.ReliefStyle.NONE)
|
||||
image = Gtk.Image.new()
|
||||
image.set_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU)
|
||||
|
@ -81,13 +85,15 @@ class NotebookTab(Gtk.EventBox):
|
|||
button.set_tooltip_text(_('Close Tab'))
|
||||
hbox.pack_start(button, False, False, 0)
|
||||
button.show()
|
||||
self.button = button
|
||||
|
||||
self.add(hbox)
|
||||
hbox.show()
|
||||
|
||||
def get_text(self):
|
||||
def get_text(self) -> str:
|
||||
return self.label.get_text()
|
||||
|
||||
def set_text(self, s):
|
||||
def set_text(self, s: str) -> None:
|
||||
self.label.set_text(s)
|
||||
|
||||
|
||||
|
@ -99,7 +105,7 @@ class FileInfo:
|
|||
# name of codec used to translate the file contents to unicode text
|
||||
self.encoding = encoding
|
||||
# the VCS object
|
||||
self.vcs = vcs
|
||||
self.vcs: VcsInterface = vcs
|
||||
# revision used to retrieve file from the VCS
|
||||
self.revision = revision
|
||||
# alternate text to display instead of the actual file name
|
||||
|
@ -147,7 +153,7 @@ class Diffuse(Gtk.Window):
|
|||
self.emit(s)
|
||||
|
||||
# creates an appropriate title for the pane header
|
||||
def updateTitle(self):
|
||||
def updateTitle(self) -> None:
|
||||
ss = []
|
||||
info = self.info
|
||||
if info.label is not None:
|
||||
|
@ -166,7 +172,7 @@ class Diffuse(Gtk.Window):
|
|||
self.emit('title_changed')
|
||||
|
||||
# set num edits
|
||||
def setEdits(self, has_edits):
|
||||
def setEdits(self, has_edits: bool) -> None:
|
||||
if self.has_edits != has_edits:
|
||||
self.has_edits = has_edits
|
||||
self.updateTitle()
|
||||
|
@ -198,8 +204,8 @@ class Diffuse(Gtk.Window):
|
|||
self.show_all()
|
||||
|
||||
# set the cursor label
|
||||
def updateCursor(self, viewer, f):
|
||||
if viewer.mode == CHAR_MODE and viewer.current_pane == f:
|
||||
def updateCursor(self, viewer: FileDiffViewerBase, f: int) -> None:
|
||||
if viewer.mode == EditMode.CHAR and viewer.current_pane == f:
|
||||
# # TODO: Find a fix for the column bug (resizing issue when editing a line)
|
||||
# j = viewer.current_char
|
||||
# if j > 0:
|
||||
|
@ -212,7 +218,7 @@ class Diffuse(Gtk.Window):
|
|||
self.cursor.set_text(s)
|
||||
|
||||
# set the format label
|
||||
def setFormat(self, s):
|
||||
def setFormat(self, s: LineEnding) -> None:
|
||||
v = []
|
||||
if s & LineEnding.DOS_FORMAT:
|
||||
v.append('DOS')
|
||||
|
@ -223,19 +229,19 @@ class Diffuse(Gtk.Window):
|
|||
self.format.set_text('/'.join(v))
|
||||
|
||||
# set the format label
|
||||
def setEncoding(self, s):
|
||||
def setEncoding(self, s: str) -> None:
|
||||
if s is None:
|
||||
s = ''
|
||||
self.encoding.set_text(s)
|
||||
|
||||
def __init__(self, n, prefs, title):
|
||||
FileDiffViewerBase.__init__(self, n, prefs)
|
||||
def __init__(self, n: int, prefs: Preferences, title: str) -> None:
|
||||
super().__init__(n, prefs)
|
||||
|
||||
self.title = title
|
||||
self.status = ''
|
||||
self.status: Optional[str] = ''
|
||||
|
||||
self.headers = []
|
||||
self.footers = []
|
||||
self.headers: List[Diffuse.FileDiffViewer.PaneHeader] = []
|
||||
self.footers: List[Diffuse.FileDiffViewer.PaneFooter] = []
|
||||
for i in range(n):
|
||||
# pane header
|
||||
w = Diffuse.FileDiffViewer.PaneHeader()
|
||||
|
@ -272,7 +278,7 @@ class Diffuse(Gtk.Window):
|
|||
|
||||
# convenience method to request confirmation before loading a file if
|
||||
# it will cause existing edits to be lost
|
||||
def loadFromInfo(self, f, info):
|
||||
def loadFromInfo(self, f: int, info: FileInfo) -> None:
|
||||
if self.headers[f].has_edits:
|
||||
# warn users of any unsaved changes they might lose
|
||||
dialog = Gtk.MessageDialog(
|
||||
|
@ -358,7 +364,7 @@ class Diffuse(Gtk.Window):
|
|||
# load a new file into pane 'f'
|
||||
# 'info' indicates the name of the file and how to retrieve it from the
|
||||
# version control system if applicable
|
||||
def load(self, f, info):
|
||||
def load(self, f: int, info: FileInfo) -> None:
|
||||
name = info.name
|
||||
encoding = info.encoding
|
||||
stat = None
|
||||
|
@ -371,7 +377,7 @@ class Diffuse(Gtk.Window):
|
|||
if rev is None:
|
||||
# load the contents of a plain file
|
||||
with open(name, 'rb') as fd:
|
||||
s = fd.read()
|
||||
contents = fd.read()
|
||||
# get the file's modification times so we can detect changes
|
||||
stat = os.stat(name)
|
||||
else:
|
||||
|
@ -379,12 +385,12 @@ class Diffuse(Gtk.Window):
|
|||
raise IOError('Not under version control.')
|
||||
fullname = os.path.abspath(name)
|
||||
# retrieve the revision from the version control system
|
||||
s = info.vcs.getRevision(self.prefs, fullname, rev)
|
||||
contents = info.vcs.getRevision(self.prefs, fullname, rev)
|
||||
# convert file contents to unicode
|
||||
if encoding is None:
|
||||
s, encoding = self.prefs.convertToUnicode(s)
|
||||
s, encoding = self.prefs.convertToUnicode(contents)
|
||||
else:
|
||||
s = str(s, encoding=encoding)
|
||||
s = str(contents, encoding=encoding)
|
||||
ss = utils.splitlines(s)
|
||||
except (IOError, OSError, UnicodeDecodeError, LookupError):
|
||||
# FIXME: this can occur before the toplevel window is drawn
|
||||
|
@ -408,7 +414,7 @@ class Diffuse(Gtk.Window):
|
|||
self.setSyntax(syntax)
|
||||
|
||||
# load a new file into pane 'f'
|
||||
def open_file(self, f, reload=False):
|
||||
def open_file(self, f: int, reload: bool = False) -> None:
|
||||
h = self.headers[f]
|
||||
info = h.info
|
||||
if not reload:
|
||||
|
@ -487,7 +493,7 @@ class Diffuse(Gtk.Window):
|
|||
pass
|
||||
|
||||
# save contents of pane 'f' to file
|
||||
def save_file(self, f, save_as=False):
|
||||
def save_file(self, f: int, save_as: bool = False) -> bool:
|
||||
h = self.headers[f]
|
||||
info = h.info
|
||||
name, encoding, rev, label = info.name, info.encoding, info.revision, info.label
|
||||
|
@ -642,15 +648,15 @@ class Diffuse(Gtk.Window):
|
|||
self.updateStatus()
|
||||
|
||||
# update the viewer's current status message
|
||||
def updateStatus(self):
|
||||
if self.mode == LINE_MODE:
|
||||
def updateStatus(self) -> None:
|
||||
if self.mode == EditMode.LINE:
|
||||
s = _(
|
||||
'Press the enter key or double click to edit. Press the space bar or use the '
|
||||
'RMB menu to manually align.'
|
||||
)
|
||||
elif self.mode == CHAR_MODE:
|
||||
elif self.mode == EditMode.CHAR:
|
||||
s = _('Press the escape key to finish editing.')
|
||||
elif self.mode == ALIGN_MODE:
|
||||
elif self.mode == EditMode.ALIGN:
|
||||
s = _(
|
||||
'Select target line and press the space bar to align. Press the escape key to '
|
||||
'cancel.'
|
||||
|
@ -661,7 +667,7 @@ class Diffuse(Gtk.Window):
|
|||
self.emit('status_changed', s)
|
||||
|
||||
# gets the status bar text
|
||||
def getStatus(self):
|
||||
def getStatus(self) -> Optional[str]:
|
||||
return self.status
|
||||
|
||||
# callback to display the cursor in a pane
|
||||
|
@ -674,7 +680,7 @@ class Diffuse(Gtk.Window):
|
|||
self.footers[f].setFormat(fmt)
|
||||
|
||||
def __init__(self, rc_dir):
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL)
|
||||
super().__init__(type=Gtk.WindowType.TOPLEVEL)
|
||||
|
||||
self.prefs = Preferences(os.path.join(rc_dir, 'prefs'))
|
||||
# number of created viewers (used to label some tabs)
|
||||
|
@ -702,8 +708,8 @@ class Diffuse(Gtk.Window):
|
|||
self.connect('window-state-event', self.window_state_cb)
|
||||
|
||||
# search history is application wide
|
||||
self.search_pattern = None
|
||||
self.search_history = []
|
||||
self.search_pattern: Optional[str] = None
|
||||
self.search_history: List[str] = []
|
||||
|
||||
self.connect('delete-event', self.delete_cb)
|
||||
accel_group = Gtk.AccelGroup()
|
||||
|
@ -968,7 +974,7 @@ class Diffuse(Gtk.Window):
|
|||
)
|
||||
|
||||
# load state information that should persist across sessions
|
||||
def loadState(self, statepath):
|
||||
def loadState(self, statepath: str) -> None:
|
||||
if os.path.isfile(statepath):
|
||||
try:
|
||||
f = open(statepath, 'r')
|
||||
|
@ -998,7 +1004,7 @@ class Diffuse(Gtk.Window):
|
|||
self.maximize()
|
||||
|
||||
# save state information that should persist across sessions
|
||||
def saveState(self, statepath):
|
||||
def saveState(self, statepath: str) -> None:
|
||||
try:
|
||||
ss = []
|
||||
for k, v in self.bool_state.items():
|
||||
|
@ -1025,7 +1031,7 @@ class Diffuse(Gtk.Window):
|
|||
|
||||
# returns True if the list of viewers can be closed. The user will be
|
||||
# given a chance to save any modified files before this method completes.
|
||||
def confirmCloseViewers(self, viewers):
|
||||
def confirmCloseViewers(self, viewers: List[FileDiffViewer]) -> bool:
|
||||
# make a list of modified files
|
||||
model = Gtk.ListStore.new([
|
||||
GObject.TYPE_BOOLEAN,
|
||||
|
@ -1144,12 +1150,12 @@ class Diffuse(Gtk.Window):
|
|||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
||||
# update window's title
|
||||
def updateTitle(self, viewer):
|
||||
def updateTitle(self, viewer: FileDiffViewer) -> None:
|
||||
title = self.notebook.get_tab_label(viewer).get_text()
|
||||
self.set_title(f'{title} - {constants.APP_NAME}')
|
||||
|
||||
# update the message in the status bar
|
||||
def setStatus(self, s):
|
||||
def setStatus(self, s: Optional[str]) -> None:
|
||||
sb = self.statusbar
|
||||
context = sb.get_context_id('Message')
|
||||
sb.pop(context)
|
||||
|
@ -1358,7 +1364,7 @@ class Diffuse(Gtk.Window):
|
|||
return True
|
||||
|
||||
# returns the currently focused viewer
|
||||
def getCurrentViewer(self) -> Optional[Gtk.Widget]:
|
||||
def getCurrentViewer(self) -> FileDiffViewer:
|
||||
return self.notebook.get_nth_page(self.notebook.get_current_page())
|
||||
|
||||
# callback for the open file menu item
|
||||
|
@ -1504,7 +1510,7 @@ class Diffuse(Gtk.Window):
|
|||
|
||||
# request search parameters if force=True and then perform a search in the
|
||||
# current viewer pane
|
||||
def find(self, force, reverse):
|
||||
def find(self, force: bool, reverse: bool) -> None:
|
||||
viewer = self.getCurrentViewer()
|
||||
if force or self.search_pattern is None:
|
||||
# construct search dialog
|
||||
|
|
|
@ -24,7 +24,7 @@ import shlex
|
|||
import sys
|
||||
|
||||
from gettext import gettext as _
|
||||
from typing import List
|
||||
from typing import Any, Dict, Final, List, Optional, Tuple
|
||||
|
||||
from diffuse import constants
|
||||
from diffuse import utils
|
||||
|
@ -36,16 +36,16 @@ from gi.repository import Gtk # type: ignore # noqa: E402
|
|||
|
||||
# class to store preferences and construct a dialogue for manipulating them
|
||||
class Preferences:
|
||||
def __init__(self, path):
|
||||
self.bool_prefs = {}
|
||||
self.int_prefs = {}
|
||||
self.string_prefs = {}
|
||||
self.int_prefs_min = {}
|
||||
self.int_prefs_max = {}
|
||||
self.string_prefs_enums = {}
|
||||
def __init__(self, path: str) -> None:
|
||||
self.path = path
|
||||
self.bool_prefs: Dict[str, bool] = {}
|
||||
self.string_prefs: Dict[str, str] = {}
|
||||
self.int_prefs: Dict[str, int] = {}
|
||||
self.int_prefs_min: Dict[str, int] = {}
|
||||
self.int_prefs_max: Dict[str, int] = {}
|
||||
|
||||
# find available encodings
|
||||
self.encodings = sorted(set(encodings.aliases.aliases.values()))
|
||||
self.encodings: List[Optional[str]] = sorted(set(encodings.aliases.aliases.values()))
|
||||
|
||||
if utils.isWindows():
|
||||
svk_bin = 'svk.bat'
|
||||
|
@ -54,7 +54,7 @@ class Preferences:
|
|||
|
||||
auto_detect_codecs = ['utf_8', 'utf_16', 'latin_1']
|
||||
e = utils.norm_encoding(sys.getfilesystemencoding())
|
||||
if e not in auto_detect_codecs:
|
||||
if e is not None and e not in auto_detect_codecs:
|
||||
# insert after UTF-8 as the default encoding may prevent UTF-8 from
|
||||
# being tried
|
||||
auto_detect_codecs.insert(2, e)
|
||||
|
@ -124,7 +124,7 @@ class Preferences:
|
|||
]
|
||||
|
||||
# conditions used to determine if a preference should be greyed out
|
||||
self.disable_when = {
|
||||
self.disable_when: Final[Dict[str, Tuple[str, bool]]] = {
|
||||
'display_right_margin': ('display_show_right_margin', False),
|
||||
'display_ignore_whitespace_changes': ('display_ignore_whitespace', True),
|
||||
'display_ignore_blanklines': ('display_ignore_whitespace', True),
|
||||
|
@ -165,9 +165,9 @@ class Preferences:
|
|||
_('Version control system search order')
|
||||
]
|
||||
]
|
||||
vcs_folders_template = ['FolderSet']
|
||||
vcs_folders_template: List[Any] = ['FolderSet']
|
||||
for key, name, cmd in vcs:
|
||||
temp = ['List']
|
||||
temp: List[Any] = ['List']
|
||||
if key == 'rcs':
|
||||
# RCS uses multiple commands
|
||||
temp.extend([['File', key + '_bin_co', 'co', _('"co" command')],
|
||||
|
@ -196,8 +196,8 @@ class Preferences:
|
|||
self.default_bool_prefs = self.bool_prefs.copy()
|
||||
self.default_int_prefs = self.int_prefs.copy()
|
||||
self.default_string_prefs = self.string_prefs.copy()
|
||||
|
||||
# load the user's preferences
|
||||
self.path = path
|
||||
if os.path.isfile(self.path):
|
||||
try:
|
||||
with open(self.path, 'r', encoding='utf-8') as f:
|
||||
|
@ -255,16 +255,16 @@ class Preferences:
|
|||
|
||||
# display the dialogue and update the preference values if the accept
|
||||
# button was pressed
|
||||
def runDialog(self, parent):
|
||||
def runDialog(self, parent: Gtk.Widget) -> None:
|
||||
dialog = Gtk.Dialog(_('Preferences'), parent=parent, destroy_with_parent=True)
|
||||
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT)
|
||||
dialog.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
||||
|
||||
widgets = {}
|
||||
widgets: Dict[str, Gtk.Widget] = {}
|
||||
w = self._buildPrefsDialog(parent, widgets, self.template)
|
||||
# disable any preferences than are not relevant
|
||||
for k, v in self.disable_when.items():
|
||||
p, t = v
|
||||
for k, tuple_value in self.disable_when.items():
|
||||
p, t = tuple_value
|
||||
if widgets[p].get_active() == t:
|
||||
widgets[k].set_sensitive(False)
|
||||
dialog.vbox.add(w)
|
||||
|
@ -280,15 +280,15 @@ class Preferences:
|
|||
self.string_prefs[k] = utils.null_to_empty(widgets[k].get_text())
|
||||
try:
|
||||
ss = []
|
||||
for k, v in self.bool_prefs.items():
|
||||
if v != self.default_bool_prefs[k]:
|
||||
ss.append(f'{k} {v}\n')
|
||||
for k, v in self.int_prefs.items():
|
||||
if v != self.default_int_prefs[k]:
|
||||
ss.append(f'{k} {v}\n')
|
||||
for k, v in self.string_prefs.items():
|
||||
if v != self.default_string_prefs[k]:
|
||||
v_escaped = v.replace('\\', '\\\\').replace('"', '\\"')
|
||||
for k, bool_value in self.bool_prefs.items():
|
||||
if bool_value != self.default_bool_prefs[k]:
|
||||
ss.append(f'{k} {bool_value}\n')
|
||||
for k, int_value in self.int_prefs.items():
|
||||
if int_value != self.default_int_prefs[k]:
|
||||
ss.append(f'{k} {int_value}\n')
|
||||
for k, str_value in self.string_prefs.items():
|
||||
if str_value != self.default_string_prefs[k]:
|
||||
v_escaped = str_value.replace('\\', '\\\\').replace('"', '\\"')
|
||||
ss.append(f'{k} "{v_escaped}"\n')
|
||||
ss.sort()
|
||||
with open(self.path, 'w', encoding='utf-8') as f:
|
||||
|
@ -384,7 +384,7 @@ class Preferences:
|
|||
def setString(self, name: str, value: str) -> None:
|
||||
self.string_prefs[name] = value
|
||||
|
||||
def getEncodings(self):
|
||||
def getEncodings(self) -> List[Optional[str]]:
|
||||
return self.encodings
|
||||
|
||||
def _getDefaultEncodings(self) -> List[str]:
|
||||
|
@ -439,7 +439,7 @@ class Preferences:
|
|||
|
||||
# text entry widget with a button to help pick file names
|
||||
class _FileEntry(Gtk.Box):
|
||||
def __init__(self, parent, title):
|
||||
def __init__(self, parent: Gtk.Widget, title: str) -> None:
|
||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.toplevel = parent
|
||||
self.title = title
|
||||
|
@ -456,7 +456,7 @@ class _FileEntry(Gtk.Box):
|
|||
button.show()
|
||||
|
||||
# action performed when the pick file button is pressed
|
||||
def chooseFile(self, widget):
|
||||
def chooseFile(self, widget: Gtk.Widget) -> None:
|
||||
dialog = Gtk.FileChooserDialog(
|
||||
self.title,
|
||||
self.toplevel,
|
||||
|
|
|
@ -31,7 +31,7 @@ import shlex
|
|||
|
||||
from distutils import util
|
||||
from gettext import gettext as _
|
||||
from typing import Final
|
||||
from typing import Dict, Final, List, Optional, Pattern, Set, Tuple
|
||||
|
||||
from diffuse import utils
|
||||
|
||||
|
@ -45,134 +45,133 @@ class Resources:
|
|||
# default keybindings
|
||||
self.keybindings = {}
|
||||
self.keybindings_lookup = {}
|
||||
set_binding = self.setKeyBinding
|
||||
set_binding('menu', 'open_file', 'Ctrl+o')
|
||||
set_binding('menu', 'open_file_in_new_tab', 'Ctrl+t')
|
||||
set_binding('menu', 'open_modified_files', 'Shift+Ctrl+O')
|
||||
set_binding('menu', 'open_commit', 'Shift+Ctrl+T')
|
||||
set_binding('menu', 'reload_file', 'Shift+Ctrl+R')
|
||||
set_binding('menu', 'save_file', 'Ctrl+s')
|
||||
set_binding('menu', 'save_file_as', 'Shift+Ctrl+A')
|
||||
set_binding('menu', 'save_all', 'Shift+Ctrl+S')
|
||||
set_binding('menu', 'new_2_way_file_merge', 'Ctrl+2')
|
||||
set_binding('menu', 'new_3_way_file_merge', 'Ctrl+3')
|
||||
set_binding('menu', 'new_n_way_file_merge', 'Ctrl+4')
|
||||
set_binding('menu', 'close_tab', 'Ctrl+w')
|
||||
set_binding('menu', 'undo_close_tab', 'Shift+Ctrl+W')
|
||||
set_binding('menu', 'quit', 'Ctrl+q')
|
||||
set_binding('menu', 'undo', 'Ctrl+z')
|
||||
set_binding('menu', 'redo', 'Shift+Ctrl+Z')
|
||||
set_binding('menu', 'cut', 'Ctrl+x')
|
||||
set_binding('menu', 'copy', 'Ctrl+c')
|
||||
set_binding('menu', 'paste', 'Ctrl+v')
|
||||
set_binding('menu', 'select_all', 'Ctrl+a')
|
||||
set_binding('menu', 'clear_edits', 'Ctrl+r')
|
||||
set_binding('menu', 'dismiss_all_edits', 'Ctrl+d')
|
||||
set_binding('menu', 'find', 'Ctrl+f')
|
||||
set_binding('menu', 'find_next', 'Ctrl+g')
|
||||
set_binding('menu', 'find_previous', 'Shift+Ctrl+G')
|
||||
set_binding('menu', 'go_to_line', 'Shift+Ctrl+L')
|
||||
set_binding('menu', 'realign_all', 'Ctrl+l')
|
||||
set_binding('menu', 'isolate', 'Ctrl+i')
|
||||
set_binding('menu', 'first_difference', 'Shift+Ctrl+Up')
|
||||
set_binding('menu', 'previous_difference', 'Ctrl+Up')
|
||||
set_binding('menu', 'next_difference', 'Ctrl+Down')
|
||||
set_binding('menu', 'last_difference', 'Shift+Ctrl+Down')
|
||||
set_binding('menu', 'first_tab', 'Shift+Ctrl+Page_Up')
|
||||
set_binding('menu', 'previous_tab', 'Ctrl+Page_Up')
|
||||
set_binding('menu', 'next_tab', 'Ctrl+Page_Down')
|
||||
set_binding('menu', 'last_tab', 'Shift+Ctrl+Page_Down')
|
||||
set_binding('menu', 'shift_pane_right', 'Shift+Ctrl+parenright')
|
||||
set_binding('menu', 'shift_pane_left', 'Shift+Ctrl+parenleft')
|
||||
set_binding('menu', 'convert_to_upper_case', 'Ctrl+u')
|
||||
set_binding('menu', 'convert_to_lower_case', 'Shift+Ctrl+U')
|
||||
set_binding('menu', 'sort_lines_in_ascending_order', 'Ctrl+y')
|
||||
set_binding('menu', 'sort_lines_in_descending_order', 'Shift+Ctrl+Y')
|
||||
set_binding('menu', 'remove_trailing_white_space', 'Ctrl+k')
|
||||
set_binding('menu', 'convert_tabs_to_spaces', 'Ctrl+b')
|
||||
set_binding('menu', 'convert_leading_spaces_to_tabs', 'Shift+Ctrl+B')
|
||||
set_binding('menu', 'increase_indenting', 'Shift+Ctrl+greater')
|
||||
set_binding('menu', 'decrease_indenting', 'Shift+Ctrl+less')
|
||||
set_binding('menu', 'convert_to_dos', 'Shift+Ctrl+E')
|
||||
set_binding('menu', 'convert_to_mac', 'Shift+Ctrl+C')
|
||||
set_binding('menu', 'convert_to_unix', 'Ctrl+e')
|
||||
set_binding('menu', 'copy_selection_right', 'Shift+Ctrl+Right')
|
||||
set_binding('menu', 'copy_selection_left', 'Shift+Ctrl+Left')
|
||||
set_binding('menu', 'copy_left_into_selection', 'Ctrl+Right')
|
||||
set_binding('menu', 'copy_right_into_selection', 'Ctrl+Left')
|
||||
set_binding('menu', 'merge_from_left_then_right', 'Ctrl+m')
|
||||
set_binding('menu', 'merge_from_right_then_left', 'Shift+Ctrl+M')
|
||||
set_binding('menu', 'help_contents', 'F1')
|
||||
set_binding('line_mode', 'enter_align_mode', 'space')
|
||||
set_binding('line_mode', 'enter_character_mode', 'Return')
|
||||
set_binding('line_mode', 'enter_character_mode', 'KP_Enter')
|
||||
set_binding('line_mode', 'first_line', 'Home')
|
||||
set_binding('line_mode', 'first_line', 'g')
|
||||
set_binding('line_mode', 'extend_first_line', 'Shift+Home')
|
||||
set_binding('line_mode', 'last_line', 'End')
|
||||
set_binding('line_mode', 'last_line', 'Shift+G')
|
||||
set_binding('line_mode', 'extend_last_line', 'Shift+End')
|
||||
set_binding('line_mode', 'up', 'Up')
|
||||
set_binding('line_mode', 'up', 'k')
|
||||
set_binding('line_mode', 'extend_up', 'Shift+Up')
|
||||
set_binding('line_mode', 'extend_up', 'Shift+K')
|
||||
set_binding('line_mode', 'down', 'Down')
|
||||
set_binding('line_mode', 'down', 'j')
|
||||
set_binding('line_mode', 'extend_down', 'Shift+Down')
|
||||
set_binding('line_mode', 'extend_down', 'Shift+J')
|
||||
set_binding('line_mode', 'left', 'Left')
|
||||
set_binding('line_mode', 'left', 'h')
|
||||
set_binding('line_mode', 'extend_left', 'Shift+Left')
|
||||
set_binding('line_mode', 'right', 'Right')
|
||||
set_binding('line_mode', 'right', 'l')
|
||||
set_binding('line_mode', 'extend_right', 'Shift+Right')
|
||||
set_binding('line_mode', 'page_up', 'Page_Up')
|
||||
set_binding('line_mode', 'page_up', 'Ctrl+u')
|
||||
set_binding('line_mode', 'extend_page_up', 'Shift+Page_Up')
|
||||
set_binding('line_mode', 'extend_page_up', 'Shift+Ctrl+U')
|
||||
set_binding('line_mode', 'page_down', 'Page_Down')
|
||||
set_binding('line_mode', 'page_down', 'Ctrl+d')
|
||||
set_binding('line_mode', 'extend_page_down', 'Shift+Page_Down')
|
||||
set_binding('line_mode', 'extend_page_down', 'Shift+Ctrl+D')
|
||||
set_binding('line_mode', 'delete_text', 'BackSpace')
|
||||
set_binding('line_mode', 'delete_text', 'Delete')
|
||||
set_binding('line_mode', 'delete_text', 'x')
|
||||
set_binding('line_mode', 'clear_edits', 'r')
|
||||
set_binding('line_mode', 'isolate', 'i')
|
||||
set_binding('line_mode', 'first_difference', 'Ctrl+Home')
|
||||
set_binding('line_mode', 'first_difference', 'Shift+P')
|
||||
set_binding('line_mode', 'previous_difference', 'p')
|
||||
set_binding('line_mode', 'next_difference', 'n')
|
||||
set_binding('line_mode', 'last_difference', 'Ctrl+End')
|
||||
set_binding('line_mode', 'last_difference', 'Shift+N')
|
||||
# set_binding('line_mode', 'copy_selection_right', 'Shift+L')
|
||||
# set_binding('line_mode', 'copy_selection_left', 'Shift+H')
|
||||
set_binding('line_mode', 'copy_left_into_selection', 'Shift+L')
|
||||
set_binding('line_mode', 'copy_right_into_selection', 'Shift+H')
|
||||
set_binding('line_mode', 'merge_from_left_then_right', 'm')
|
||||
set_binding('line_mode', 'merge_from_right_then_left', 'Shift+M')
|
||||
set_binding('align_mode', 'enter_line_mode', 'Escape')
|
||||
set_binding('align_mode', 'align', 'space')
|
||||
set_binding('align_mode', 'enter_character_mode', 'Return')
|
||||
set_binding('align_mode', 'enter_character_mode', 'KP_Enter')
|
||||
set_binding('align_mode', 'first_line', 'g')
|
||||
set_binding('align_mode', 'last_line', 'Shift+G')
|
||||
set_binding('align_mode', 'up', 'Up')
|
||||
set_binding('align_mode', 'up', 'k')
|
||||
set_binding('align_mode', 'down', 'Down')
|
||||
set_binding('align_mode', 'down', 'j')
|
||||
set_binding('align_mode', 'left', 'Left')
|
||||
set_binding('align_mode', 'left', 'h')
|
||||
set_binding('align_mode', 'right', 'Right')
|
||||
set_binding('align_mode', 'right', 'l')
|
||||
set_binding('align_mode', 'page_up', 'Page_Up')
|
||||
set_binding('align_mode', 'page_up', 'Ctrl+u')
|
||||
set_binding('align_mode', 'page_down', 'Page_Down')
|
||||
set_binding('align_mode', 'page_down', 'Ctrl+d')
|
||||
set_binding('character_mode', 'enter_line_mode', 'Escape')
|
||||
self.setKeyBinding('menu', 'open_file', 'Ctrl+o')
|
||||
self.setKeyBinding('menu', 'open_file_in_new_tab', 'Ctrl+t')
|
||||
self.setKeyBinding('menu', 'open_modified_files', 'Shift+Ctrl+O')
|
||||
self.setKeyBinding('menu', 'open_commit', 'Shift+Ctrl+T')
|
||||
self.setKeyBinding('menu', 'reload_file', 'Shift+Ctrl+R')
|
||||
self.setKeyBinding('menu', 'save_file', 'Ctrl+s')
|
||||
self.setKeyBinding('menu', 'save_file_as', 'Shift+Ctrl+A')
|
||||
self.setKeyBinding('menu', 'save_all', 'Shift+Ctrl+S')
|
||||
self.setKeyBinding('menu', 'new_2_way_file_merge', 'Ctrl+2')
|
||||
self.setKeyBinding('menu', 'new_3_way_file_merge', 'Ctrl+3')
|
||||
self.setKeyBinding('menu', 'new_n_way_file_merge', 'Ctrl+4')
|
||||
self.setKeyBinding('menu', 'close_tab', 'Ctrl+w')
|
||||
self.setKeyBinding('menu', 'undo_close_tab', 'Shift+Ctrl+W')
|
||||
self.setKeyBinding('menu', 'quit', 'Ctrl+q')
|
||||
self.setKeyBinding('menu', 'undo', 'Ctrl+z')
|
||||
self.setKeyBinding('menu', 'redo', 'Shift+Ctrl+Z')
|
||||
self.setKeyBinding('menu', 'cut', 'Ctrl+x')
|
||||
self.setKeyBinding('menu', 'copy', 'Ctrl+c')
|
||||
self.setKeyBinding('menu', 'paste', 'Ctrl+v')
|
||||
self.setKeyBinding('menu', 'select_all', 'Ctrl+a')
|
||||
self.setKeyBinding('menu', 'clear_edits', 'Ctrl+r')
|
||||
self.setKeyBinding('menu', 'dismiss_all_edits', 'Ctrl+d')
|
||||
self.setKeyBinding('menu', 'find', 'Ctrl+f')
|
||||
self.setKeyBinding('menu', 'find_next', 'Ctrl+g')
|
||||
self.setKeyBinding('menu', 'find_previous', 'Shift+Ctrl+G')
|
||||
self.setKeyBinding('menu', 'go_to_line', 'Shift+Ctrl+L')
|
||||
self.setKeyBinding('menu', 'realign_all', 'Ctrl+l')
|
||||
self.setKeyBinding('menu', 'isolate', 'Ctrl+i')
|
||||
self.setKeyBinding('menu', 'first_difference', 'Shift+Ctrl+Up')
|
||||
self.setKeyBinding('menu', 'previous_difference', 'Ctrl+Up')
|
||||
self.setKeyBinding('menu', 'next_difference', 'Ctrl+Down')
|
||||
self.setKeyBinding('menu', 'last_difference', 'Shift+Ctrl+Down')
|
||||
self.setKeyBinding('menu', 'first_tab', 'Shift+Ctrl+Page_Up')
|
||||
self.setKeyBinding('menu', 'previous_tab', 'Ctrl+Page_Up')
|
||||
self.setKeyBinding('menu', 'next_tab', 'Ctrl+Page_Down')
|
||||
self.setKeyBinding('menu', 'last_tab', 'Shift+Ctrl+Page_Down')
|
||||
self.setKeyBinding('menu', 'shift_pane_right', 'Shift+Ctrl+parenright')
|
||||
self.setKeyBinding('menu', 'shift_pane_left', 'Shift+Ctrl+parenleft')
|
||||
self.setKeyBinding('menu', 'convert_to_upper_case', 'Ctrl+u')
|
||||
self.setKeyBinding('menu', 'convert_to_lower_case', 'Shift+Ctrl+U')
|
||||
self.setKeyBinding('menu', 'sort_lines_in_ascending_order', 'Ctrl+y')
|
||||
self.setKeyBinding('menu', 'sort_lines_in_descending_order', 'Shift+Ctrl+Y')
|
||||
self.setKeyBinding('menu', 'remove_trailing_white_space', 'Ctrl+k')
|
||||
self.setKeyBinding('menu', 'convert_tabs_to_spaces', 'Ctrl+b')
|
||||
self.setKeyBinding('menu', 'convert_leading_spaces_to_tabs', 'Shift+Ctrl+B')
|
||||
self.setKeyBinding('menu', 'increase_indenting', 'Shift+Ctrl+greater')
|
||||
self.setKeyBinding('menu', 'decrease_indenting', 'Shift+Ctrl+less')
|
||||
self.setKeyBinding('menu', 'convert_to_dos', 'Shift+Ctrl+E')
|
||||
self.setKeyBinding('menu', 'convert_to_mac', 'Shift+Ctrl+C')
|
||||
self.setKeyBinding('menu', 'convert_to_unix', 'Ctrl+e')
|
||||
self.setKeyBinding('menu', 'copy_selection_right', 'Shift+Ctrl+Right')
|
||||
self.setKeyBinding('menu', 'copy_selection_left', 'Shift+Ctrl+Left')
|
||||
self.setKeyBinding('menu', 'copy_left_into_selection', 'Ctrl+Right')
|
||||
self.setKeyBinding('menu', 'copy_right_into_selection', 'Ctrl+Left')
|
||||
self.setKeyBinding('menu', 'merge_from_left_then_right', 'Ctrl+m')
|
||||
self.setKeyBinding('menu', 'merge_from_right_then_left', 'Shift+Ctrl+M')
|
||||
self.setKeyBinding('menu', 'help_contents', 'F1')
|
||||
self.setKeyBinding('line_mode', 'enter_align_mode', 'space')
|
||||
self.setKeyBinding('line_mode', 'enter_character_mode', 'Return')
|
||||
self.setKeyBinding('line_mode', 'enter_character_mode', 'KP_Enter')
|
||||
self.setKeyBinding('line_mode', 'first_line', 'Home')
|
||||
self.setKeyBinding('line_mode', 'first_line', 'g')
|
||||
self.setKeyBinding('line_mode', 'extend_first_line', 'Shift+Home')
|
||||
self.setKeyBinding('line_mode', 'last_line', 'End')
|
||||
self.setKeyBinding('line_mode', 'last_line', 'Shift+G')
|
||||
self.setKeyBinding('line_mode', 'extend_last_line', 'Shift+End')
|
||||
self.setKeyBinding('line_mode', 'up', 'Up')
|
||||
self.setKeyBinding('line_mode', 'up', 'k')
|
||||
self.setKeyBinding('line_mode', 'extend_up', 'Shift+Up')
|
||||
self.setKeyBinding('line_mode', 'extend_up', 'Shift+K')
|
||||
self.setKeyBinding('line_mode', 'down', 'Down')
|
||||
self.setKeyBinding('line_mode', 'down', 'j')
|
||||
self.setKeyBinding('line_mode', 'extend_down', 'Shift+Down')
|
||||
self.setKeyBinding('line_mode', 'extend_down', 'Shift+J')
|
||||
self.setKeyBinding('line_mode', 'left', 'Left')
|
||||
self.setKeyBinding('line_mode', 'left', 'h')
|
||||
self.setKeyBinding('line_mode', 'extend_left', 'Shift+Left')
|
||||
self.setKeyBinding('line_mode', 'right', 'Right')
|
||||
self.setKeyBinding('line_mode', 'right', 'l')
|
||||
self.setKeyBinding('line_mode', 'extend_right', 'Shift+Right')
|
||||
self.setKeyBinding('line_mode', 'page_up', 'Page_Up')
|
||||
self.setKeyBinding('line_mode', 'page_up', 'Ctrl+u')
|
||||
self.setKeyBinding('line_mode', 'extend_page_up', 'Shift+Page_Up')
|
||||
self.setKeyBinding('line_mode', 'extend_page_up', 'Shift+Ctrl+U')
|
||||
self.setKeyBinding('line_mode', 'page_down', 'Page_Down')
|
||||
self.setKeyBinding('line_mode', 'page_down', 'Ctrl+d')
|
||||
self.setKeyBinding('line_mode', 'extend_page_down', 'Shift+Page_Down')
|
||||
self.setKeyBinding('line_mode', 'extend_page_down', 'Shift+Ctrl+D')
|
||||
self.setKeyBinding('line_mode', 'delete_text', 'BackSpace')
|
||||
self.setKeyBinding('line_mode', 'delete_text', 'Delete')
|
||||
self.setKeyBinding('line_mode', 'delete_text', 'x')
|
||||
self.setKeyBinding('line_mode', 'clear_edits', 'r')
|
||||
self.setKeyBinding('line_mode', 'isolate', 'i')
|
||||
self.setKeyBinding('line_mode', 'first_difference', 'Ctrl+Home')
|
||||
self.setKeyBinding('line_mode', 'first_difference', 'Shift+P')
|
||||
self.setKeyBinding('line_mode', 'previous_difference', 'p')
|
||||
self.setKeyBinding('line_mode', 'next_difference', 'n')
|
||||
self.setKeyBinding('line_mode', 'last_difference', 'Ctrl+End')
|
||||
self.setKeyBinding('line_mode', 'last_difference', 'Shift+N')
|
||||
# self.setKeyBinding('line_mode', 'copy_selection_right', 'Shift+L')
|
||||
# self.setKeyBinding('line_mode', 'copy_selection_left', 'Shift+H')
|
||||
self.setKeyBinding('line_mode', 'copy_left_into_selection', 'Shift+L')
|
||||
self.setKeyBinding('line_mode', 'copy_right_into_selection', 'Shift+H')
|
||||
self.setKeyBinding('line_mode', 'merge_from_left_then_right', 'm')
|
||||
self.setKeyBinding('line_mode', 'merge_from_right_then_left', 'Shift+M')
|
||||
self.setKeyBinding('align_mode', 'enter_line_mode', 'Escape')
|
||||
self.setKeyBinding('align_mode', 'align', 'space')
|
||||
self.setKeyBinding('align_mode', 'enter_character_mode', 'Return')
|
||||
self.setKeyBinding('align_mode', 'enter_character_mode', 'KP_Enter')
|
||||
self.setKeyBinding('align_mode', 'first_line', 'g')
|
||||
self.setKeyBinding('align_mode', 'last_line', 'Shift+G')
|
||||
self.setKeyBinding('align_mode', 'up', 'Up')
|
||||
self.setKeyBinding('align_mode', 'up', 'k')
|
||||
self.setKeyBinding('align_mode', 'down', 'Down')
|
||||
self.setKeyBinding('align_mode', 'down', 'j')
|
||||
self.setKeyBinding('align_mode', 'left', 'Left')
|
||||
self.setKeyBinding('align_mode', 'left', 'h')
|
||||
self.setKeyBinding('align_mode', 'right', 'Right')
|
||||
self.setKeyBinding('align_mode', 'right', 'l')
|
||||
self.setKeyBinding('align_mode', 'page_up', 'Page_Up')
|
||||
self.setKeyBinding('align_mode', 'page_up', 'Ctrl+u')
|
||||
self.setKeyBinding('align_mode', 'page_down', 'Page_Down')
|
||||
self.setKeyBinding('align_mode', 'page_down', 'Ctrl+d')
|
||||
self.setKeyBinding('character_mode', 'enter_line_mode', 'Escape')
|
||||
|
||||
# default colours
|
||||
self.colours = {
|
||||
self.colours: Dict[str, _Colour] = {
|
||||
'alignment': _Colour(1.0, 1.0, 0.0),
|
||||
'character_selection': _Colour(0.7, 0.7, 1.0),
|
||||
'cursor': _Colour(0.0, 0.0, 0.0),
|
||||
|
@ -192,7 +191,7 @@ class Resources:
|
|||
}
|
||||
|
||||
# default floats
|
||||
self.floats = {
|
||||
self.floats: Dict[str, float] = {
|
||||
'alignment_opacity': 1.0,
|
||||
'character_difference_opacity': 0.4,
|
||||
'character_selection_opacity': 0.4,
|
||||
|
@ -202,29 +201,29 @@ class Resources:
|
|||
}
|
||||
|
||||
# default options
|
||||
self.options = {
|
||||
self.options: Dict[str, str] = {
|
||||
'log_print_output': 'False',
|
||||
'log_print_stack': 'False',
|
||||
'use_flatpak': 'False'
|
||||
}
|
||||
|
||||
# default strings
|
||||
self.strings = {}
|
||||
self.strings: Dict[str, str] = {}
|
||||
|
||||
# syntax highlighting support
|
||||
self.syntaxes = {}
|
||||
self.syntax_file_patterns = {}
|
||||
self.syntax_magic_patterns = {}
|
||||
self.current_syntax = None
|
||||
self.syntaxes: Dict[str, _SyntaxParser] = {}
|
||||
self.syntax_file_patterns: Dict[str, Pattern] = {}
|
||||
self.syntax_magic_patterns: Dict[str, Pattern] = {}
|
||||
self.current_syntax: Optional[_SyntaxParser] = None
|
||||
|
||||
# list of imported resources files (we only import each file once)
|
||||
self.resource_files = set()
|
||||
self.resource_files: Set[str] = set()
|
||||
|
||||
# special string resources
|
||||
self.setDifferenceColours('difference_1 difference_2 difference_3')
|
||||
|
||||
# keyboard action processing
|
||||
def setKeyBinding(self, ctx, s, v):
|
||||
def setKeyBinding(self, ctx: str, s: str, v: str) -> None:
|
||||
action_tuple = (ctx, s)
|
||||
modifiers = Gdk.ModifierType(0)
|
||||
key = None
|
||||
|
@ -282,7 +281,7 @@ class Resources:
|
|||
return []
|
||||
|
||||
# colours used for indicating differences
|
||||
def setDifferenceColours(self, s):
|
||||
def setDifferenceColours(self, s: str) -> None:
|
||||
colours = s.split()
|
||||
if len(colours) > 0:
|
||||
self.difference_colours = colours
|
||||
|
@ -322,7 +321,7 @@ class Resources:
|
|||
return util.strtobool(self.getOption(option))
|
||||
|
||||
# string resources
|
||||
def getString(self, symbol):
|
||||
def getString(self, symbol: str) -> str:
|
||||
try:
|
||||
return self.strings[symbol]
|
||||
except KeyError:
|
||||
|
@ -336,7 +335,7 @@ class Resources:
|
|||
def getSyntax(self, name):
|
||||
return self.syntaxes.get(name, None)
|
||||
|
||||
def guessSyntaxForFile(self, name, ss):
|
||||
def guessSyntaxForFile(self, name: str, ss: List[str]) -> Optional[str]:
|
||||
name = os.path.basename(name)
|
||||
for key, pattern in self.syntax_file_patterns.items():
|
||||
if pattern.search(name):
|
||||
|
@ -350,7 +349,7 @@ class Resources:
|
|||
return None
|
||||
|
||||
# parse resource files
|
||||
def parse(self, file_name):
|
||||
def parse(self, file_name: str) -> None:
|
||||
# only process files once
|
||||
if file_name in self.resource_files:
|
||||
return
|
||||
|
@ -514,12 +513,12 @@ class Resources:
|
|||
file=file_name
|
||||
)
|
||||
utils.logError(f'{error_msg}: {e.msg}')
|
||||
except ValueError as e:
|
||||
except ValueError:
|
||||
error_msg = _('Value error at line {line} of {file}').format(
|
||||
line=i + 1,
|
||||
file=file_name
|
||||
)
|
||||
utils.logError(f'{error_msg}: {e.msg}')
|
||||
utils.logError(error_msg)
|
||||
except re.error:
|
||||
error_msg = _('Regex error at line {line} of {file}.')
|
||||
utils.logError(error_msg.format(line=i + 1, file=file_name))
|
||||
|
@ -558,7 +557,7 @@ class _Colour:
|
|||
class _SyntaxParser:
|
||||
# create a new state machine that begins in initial_state and classifies
|
||||
# all characters not matched by the patterns as default_token_type
|
||||
def __init__(self, initial_state, default_token_type):
|
||||
def __init__(self, initial_state: str, default_token_type: str) -> None:
|
||||
# initial state for the state machine when parsing a new file
|
||||
self.initial_state = initial_state
|
||||
# default classification of characters that are not explicitly matched
|
||||
|
@ -567,30 +566,33 @@ class _SyntaxParser:
|
|||
# mappings from a state to a list of (pattern, token_type, next_state)
|
||||
# tuples indicating the new state for the state machine when 'pattern'
|
||||
# is matched and how to classify the matched characters
|
||||
self.transitions_lookup = {initial_state: []}
|
||||
self.transitions_lookup: Dict[str, List[Tuple[Pattern, str, str]]] = {initial_state: []}
|
||||
|
||||
# Adds a new edge to the finite state machine from prev_state to
|
||||
# next_state. Characters will be identified as token_type when pattern is
|
||||
# matched. Any newly referenced state will be added. Patterns for edges
|
||||
# leaving a state will be tested in the order they were added to the finite
|
||||
# state machine.
|
||||
def addPattern(self, prev_state, next_state, token_type, pattern):
|
||||
lookup = self.transitions_lookup
|
||||
def addPattern(
|
||||
self,
|
||||
prev_state: str,
|
||||
next_state: str,
|
||||
token_type: str,
|
||||
pattern: Pattern) -> None:
|
||||
for state in prev_state, next_state:
|
||||
if state not in lookup:
|
||||
lookup[state] = []
|
||||
lookup[prev_state].append([pattern, token_type, next_state])
|
||||
if state not in self.transitions_lookup:
|
||||
self.transitions_lookup[state] = []
|
||||
self.transitions_lookup[prev_state].append((pattern, token_type, next_state))
|
||||
|
||||
# given a string and an initial state, identify the final state and tokens
|
||||
def parse(self, state_name, s):
|
||||
lookup = self.transitions_lookup
|
||||
transitions, blocks, start = lookup[state_name], [], 0
|
||||
transitions, blocks, start = self.transitions_lookup[state_name], [], 0
|
||||
while start < len(s):
|
||||
for pattern, token_type, next_state in transitions:
|
||||
m = pattern.match(s, start)
|
||||
if m is not None:
|
||||
end, state_name = m.span()[1], next_state
|
||||
transitions = lookup[state_name]
|
||||
transitions = self.transitions_lookup[state_name]
|
||||
break
|
||||
else:
|
||||
end, token_type = start + 1, self.default_token_type
|
||||
|
@ -602,4 +604,4 @@ class _SyntaxParser:
|
|||
return state_name, blocks
|
||||
|
||||
|
||||
theResources: Final[Resources] = Resources()
|
||||
theResources: Final = Resources()
|
||||
|
|
|
@ -28,6 +28,7 @@ from gettext import gettext as _
|
|||
from typing import Final, List, Optional, TextIO
|
||||
|
||||
from diffuse import constants
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.resources import theResources
|
||||
|
||||
import gi # type: ignore
|
||||
|
@ -37,7 +38,7 @@ from gi.repository import Gtk # type: ignore # noqa: E402
|
|||
|
||||
# convenience class for displaying a message dialogue
|
||||
class MessageDialog(Gtk.MessageDialog):
|
||||
def __init__(self, parent, message_type, s):
|
||||
def __init__(self, parent: Gtk.Widget, message_type: Gtk.MessageType, s: str) -> None:
|
||||
if message_type == Gtk.MessageType.ERROR:
|
||||
buttons = Gtk.ButtonsType.OK
|
||||
else:
|
||||
|
@ -54,7 +55,7 @@ class MessageDialog(Gtk.MessageDialog):
|
|||
|
||||
# widget to help pick an encoding
|
||||
class EncodingMenu(Gtk.Box):
|
||||
def __init__(self, prefs, autodetect=False):
|
||||
def __init__(self, prefs: Preferences, autodetect: bool = False) -> None:
|
||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.combobox = combobox = Gtk.ComboBoxText.new()
|
||||
self.encodings = prefs.getEncodings()[:]
|
||||
|
@ -66,12 +67,12 @@ class EncodingMenu(Gtk.Box):
|
|||
self.pack_start(combobox, False, False, 0)
|
||||
combobox.show()
|
||||
|
||||
def set_text(self, encoding):
|
||||
def set_text(self, encoding: Optional[str]) -> None:
|
||||
encoding = norm_encoding(encoding)
|
||||
if encoding in self.encodings:
|
||||
self.combobox.set_active(self.encodings.index(encoding))
|
||||
|
||||
def get_text(self):
|
||||
def get_text(self) -> Optional[str]:
|
||||
i = self.combobox.get_active()
|
||||
return self.encodings[i] if i >= 0 else None
|
||||
|
||||
|
@ -143,7 +144,7 @@ def relpath(a: str, b: str) -> str:
|
|||
|
||||
# helper function prevent files from being confused with command line options
|
||||
# by prepending './' to the basename
|
||||
def safeRelativePath(abspath1, name, prefs, cygwin_pref):
|
||||
def safeRelativePath(abspath1: str, name: str, prefs: Preferences, cygwin_pref: str) -> str:
|
||||
s = os.path.join(os.curdir, relpath(abspath1, os.path.abspath(name)))
|
||||
if isWindows():
|
||||
if prefs.getBool(cygwin_pref):
|
||||
|
@ -163,43 +164,49 @@ def _use_flatpak() -> bool:
|
|||
|
||||
|
||||
# use popen to read the output of a command
|
||||
def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
|
||||
def popenRead(
|
||||
cwd: str,
|
||||
cmd: List[str],
|
||||
prefs: Preferences,
|
||||
bash_pref: str,
|
||||
success_results: List[int] = None) -> bytes:
|
||||
if success_results is None:
|
||||
success_results = [0]
|
||||
|
||||
opt_cwd: Optional[str] = cwd
|
||||
if isWindows() and prefs.getBool(bash_pref):
|
||||
# launch the command from a bash shell is requested
|
||||
cmd = [
|
||||
prefs.convertToNativePath('/bin/bash.exe'),
|
||||
'-l',
|
||||
'-c',
|
||||
f"cd {_bash_escape(dn)}; {' '.join([ _bash_escape(arg) for arg in cmd ])}"
|
||||
f"cd {_bash_escape(cwd)}; {' '.join([ _bash_escape(arg) for arg in cmd ])}"
|
||||
]
|
||||
dn = None
|
||||
opt_cwd = None
|
||||
|
||||
# use subprocess.Popen to retrieve the file contents
|
||||
if isWindows():
|
||||
info = subprocess.STARTUPINFO()
|
||||
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
info.wShowWindow = subprocess.SW_HIDE
|
||||
info = subprocess.STARTUPINFO() # type: ignore
|
||||
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW # type: ignore
|
||||
info.wShowWindow = subprocess.SW_HIDE # type: ignore
|
||||
else:
|
||||
info = None
|
||||
|
||||
if _use_flatpak():
|
||||
cmd = ['flatpak-spawn', '--host'] + cmd
|
||||
with subprocess.Popen(
|
||||
cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=dn,
|
||||
cwd=opt_cwd,
|
||||
startupinfo=info) as proc:
|
||||
proc.stdin.close()
|
||||
proc.stderr.close()
|
||||
fd = proc.stdout
|
||||
output: bytes
|
||||
if proc.stdout is not None:
|
||||
# read the command's output
|
||||
s = fd.read()
|
||||
fd.close()
|
||||
output = proc.stdout.read()
|
||||
proc.stdout.close()
|
||||
if proc.wait() not in success_results:
|
||||
raise IOError('Command failed.')
|
||||
return s
|
||||
return output
|
||||
|
||||
|
||||
def len_minus_line_ending(s: str) -> int:
|
||||
|
@ -227,9 +234,14 @@ def _strip_eols(ss: List[str]) -> List[str]:
|
|||
|
||||
|
||||
# use popen to read the output of a command
|
||||
def popenReadLines(dn, cmd, prefs, bash_pref, success_results=None):
|
||||
def popenReadLines(
|
||||
cwd: str,
|
||||
cmd: List[str],
|
||||
prefs: Preferences,
|
||||
bash_pref: str,
|
||||
success_results: List[int] = None) -> List[str]:
|
||||
return _strip_eols(splitlines(popenRead(
|
||||
dn, cmd, prefs, bash_pref, success_results).decode('utf-8', errors='ignore')))
|
||||
cwd, cmd, prefs, bash_pref, success_results).decode('utf-8', errors='ignore')))
|
||||
|
||||
|
||||
def readconfiglines(fd: TextIO) -> List[str]:
|
||||
|
@ -321,6 +333,7 @@ class LineEnding(IntFlag):
|
|||
|
||||
Values can be used as flags in bitwise operations.'''
|
||||
|
||||
NO_FORMAT = 0
|
||||
DOS_FORMAT = 1
|
||||
MAC_FORMAT = 2
|
||||
UNIX_FORMAT = 4
|
||||
|
|
|
@ -20,13 +20,14 @@
|
|||
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
|
||||
|
||||
|
||||
# Bazaar support
|
||||
class Bzr(VcsInterface):
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
# merge conflict
|
||||
left = name + '.OTHER'
|
||||
right = name + '.THIS'
|
||||
|
@ -180,7 +181,7 @@ class Bzr(VcsInterface):
|
|||
result.append(m[k])
|
||||
return result
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
[
|
||||
|
|
|
@ -22,13 +22,14 @@ import os
|
|||
from gettext import gettext as _
|
||||
|
||||
from diffuse import utils
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.vcs.folder_set import FolderSet
|
||||
from diffuse.vcs.vcs_interface import VcsInterface
|
||||
|
||||
|
||||
# CVS support
|
||||
class Cvs(VcsInterface):
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
return [(name, 'BASE'), (name, None)]
|
||||
|
||||
def getCommitTemplate(self, prefs, rev, names):
|
||||
|
@ -85,10 +86,10 @@ class Cvs(VcsInterface):
|
|||
# sort the results
|
||||
return [modified[k] for k in sorted(modified.keys())]
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
if rev == 'BASE' and not os.path.exists(name):
|
||||
# find revision for removed files
|
||||
for s in utils.popenReadLines(
|
||||
lines = utils.popenReadLines(
|
||||
self.root,
|
||||
[
|
||||
prefs.getString('cvs_bin'),
|
||||
|
@ -97,9 +98,10 @@ class Cvs(VcsInterface):
|
|||
],
|
||||
prefs,
|
||||
'cvs_bash'
|
||||
):
|
||||
if s.startswith(' Working revision:\t-'):
|
||||
rev = s.split('\t')[1][1:]
|
||||
)
|
||||
for line in lines:
|
||||
if line.startswith(' Working revision:\t-'):
|
||||
rev = line.split('\t')[1][1:]
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
[
|
||||
|
|
|
@ -19,14 +19,17 @@
|
|||
|
||||
import os
|
||||
|
||||
from typing import List
|
||||
|
||||
from diffuse import utils
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.vcs.folder_set import FolderSet
|
||||
from diffuse.vcs.vcs_interface import VcsInterface
|
||||
|
||||
|
||||
# Darcs support
|
||||
class Darcs(VcsInterface):
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
return [(name, ''), (name, None)]
|
||||
|
||||
def _getCommitTemplate(self, prefs, names, rev):
|
||||
|
@ -135,8 +138,8 @@ class Darcs(VcsInterface):
|
|||
def getFolderTemplate(self, prefs, names):
|
||||
return self._getCommitTemplate(prefs, names, None)
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
args = [prefs.getString('darcs_bin'), 'show', 'contents']
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
args: List[str] = [prefs.getString('darcs_bin'), 'show', 'contents']
|
||||
try:
|
||||
args.extend(['-n', str(int(rev))])
|
||||
except ValueError:
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
import os
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
class FolderSet:
|
||||
'''Utility class to help support Git and Monotone.
|
||||
|
@ -27,18 +29,18 @@ class FolderSet:
|
|||
"mtn automate inventory."
|
||||
'''
|
||||
|
||||
def __init__(self, names):
|
||||
self.folders = f = []
|
||||
def __init__(self, names: List[str]) -> None:
|
||||
self.folders: List[str] = []
|
||||
for name in names:
|
||||
name = os.path.abspath(name)
|
||||
# ensure all names end with os.sep
|
||||
if not name.endswith(os.sep):
|
||||
name += os.sep
|
||||
f.append(name)
|
||||
self.folders.append(name)
|
||||
|
||||
# returns True if the given abspath is a file that should be included in
|
||||
# the interesting file subset
|
||||
def contains(self, abspath):
|
||||
def contains(self, abspath: str) -> bool:
|
||||
if not abspath.endswith(os.sep):
|
||||
abspath += os.sep
|
||||
for f in self.folders:
|
||||
|
|
|
@ -27,7 +27,7 @@ from diffuse.vcs.vcs_interface import VcsInterface
|
|||
|
||||
# Git support
|
||||
class Git(VcsInterface):
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
return [(name, 'HEAD'), (name, None)]
|
||||
|
||||
def getCommitTemplate(self, prefs, rev, names):
|
||||
|
@ -150,7 +150,7 @@ class Git(VcsInterface):
|
|||
result.append(m[k])
|
||||
return result
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
relpath = utils.relpath(self.root, os.path.abspath(name)).replace(os.sep, '/')
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
|
|
|
@ -22,6 +22,7 @@ import os
|
|||
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
|
||||
|
||||
|
@ -29,7 +30,7 @@ from diffuse.vcs.vcs_interface import VcsInterface
|
|||
# Mercurial support
|
||||
class Hg(VcsInterface):
|
||||
def __init__(self, root: str):
|
||||
VcsInterface.__init__(self, root)
|
||||
super().__init__(root)
|
||||
self.working_rev: Optional[str] = None
|
||||
|
||||
def _getPreviousRevision(self, prefs, rev):
|
||||
|
@ -51,7 +52,7 @@ class Hg(VcsInterface):
|
|||
return self.working_rev
|
||||
return f'p1({rev})'
|
||||
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
return [(name, self._getPreviousRevision(prefs, None)), (name, None)]
|
||||
|
||||
def _getCommitTemplate(self, prefs, names, cmd, rev):
|
||||
|
@ -97,7 +98,7 @@ class Hg(VcsInterface):
|
|||
def getFolderTemplate(self, prefs, names):
|
||||
return self._getCommitTemplate(prefs, names, ['status', '-q'], None)
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
[
|
||||
|
|
|
@ -21,27 +21,28 @@ import os
|
|||
import shlex
|
||||
|
||||
from diffuse import utils
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.vcs.folder_set import FolderSet
|
||||
from diffuse.vcs.vcs_interface import VcsInterface
|
||||
|
||||
|
||||
# Monotone support
|
||||
class Mtn(VcsInterface):
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
# FIXME: merge conflicts?
|
||||
return [(name, 'h:'), (name, None)]
|
||||
|
||||
def getCommitTemplate(self, prefs, rev, names):
|
||||
# build command
|
||||
vcs_bin = prefs.getString('mtn_bin')
|
||||
ss = utils.popenReadLines(
|
||||
lines = utils.popenReadLines(
|
||||
self.root,
|
||||
[vcs_bin, 'automate', 'select', '-q', rev],
|
||||
prefs,
|
||||
'mtn_bash')
|
||||
if len(ss) != 1:
|
||||
if len(lines) != 1:
|
||||
raise IOError('Ambiguous revision specifier')
|
||||
args = [vcs_bin, 'automate', 'get_revision', ss[0]]
|
||||
args = [vcs_bin, 'automate', 'get_revision', lines[0]]
|
||||
# build list of interesting files
|
||||
fs = FolderSet(names)
|
||||
pwd, isabs = os.path.abspath(os.curdir), False
|
||||
|
@ -50,15 +51,15 @@ class Mtn(VcsInterface):
|
|||
# run command
|
||||
prev = None
|
||||
removed, added, modified, renamed = {}, {}, {}, {}
|
||||
ss = utils.popenReadLines(self.root, args, prefs, 'mtn_bash')
|
||||
lines = utils.popenReadLines(self.root, args, prefs, 'mtn_bash')
|
||||
i = 0
|
||||
while i < len(ss):
|
||||
while i < len(lines):
|
||||
# process results
|
||||
s = shlex.split(ss[i])
|
||||
line_args = shlex.split(lines[i])
|
||||
i += 1
|
||||
if len(s) < 2:
|
||||
if len(line_args) < 2:
|
||||
continue
|
||||
arg, arg1 = s[0], s[1]
|
||||
arg, arg1 = line_args[0], line_args[1]
|
||||
if arg == 'old_revision' and len(arg1) > 2:
|
||||
if prev is not None:
|
||||
break
|
||||
|
@ -82,26 +83,27 @@ class Mtn(VcsInterface):
|
|||
if fs.contains(k):
|
||||
modified[arg1] = k
|
||||
elif arg == 'rename':
|
||||
s = shlex.split(ss[i])
|
||||
line_args = shlex.split(lines[i])
|
||||
i += 1
|
||||
if len(s) > 1 and s[0] == 'to':
|
||||
if len(line_args) > 1 and line_args[0] == 'to':
|
||||
# renamed file
|
||||
k0 = os.path.join(self.root, prefs.convertToNativePath(arg1))
|
||||
k1 = os.path.join(self.root, prefs.convertToNativePath(s[1]))
|
||||
k1 = os.path.join(self.root, prefs.convertToNativePath(line_args[1]))
|
||||
if fs.contains(k0) or fs.contains(k1):
|
||||
renamed[s[1]] = (arg1, k0, k1)
|
||||
renamed[line_args[1]] = (arg1, k0, k1)
|
||||
if removed or renamed:
|
||||
# remove directories
|
||||
removed_dirs = set()
|
||||
for s in utils.popenReadLines(
|
||||
lines = utils.popenReadLines(
|
||||
self.root,
|
||||
[vcs_bin, 'automate', 'get_manifest_of', prev],
|
||||
prefs,
|
||||
'mtn_bash'
|
||||
):
|
||||
s = shlex.split(s)
|
||||
if len(s) > 1 and s[0] == 'dir':
|
||||
removed_dirs.add(s[1])
|
||||
)
|
||||
for line in lines:
|
||||
line_args = shlex.split(line)
|
||||
if len(line_args) > 1 and line_args[0] == 'dir':
|
||||
removed_dirs.add(line_args[1])
|
||||
for k in removed_dirs:
|
||||
for m in removed, modified:
|
||||
if k in m:
|
||||
|
@ -216,7 +218,7 @@ class Mtn(VcsInterface):
|
|||
result.append(m[k])
|
||||
return result
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
[
|
||||
|
|
|
@ -22,12 +22,13 @@ import os
|
|||
from gettext import gettext as _
|
||||
|
||||
from diffuse import utils
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.vcs.vcs_interface import VcsInterface
|
||||
|
||||
|
||||
# RCS support
|
||||
class Rcs(VcsInterface):
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
args = [
|
||||
prefs.getString('rcs_bin_rlog'),
|
||||
'-L',
|
||||
|
@ -153,7 +154,7 @@ class Rcs(VcsInterface):
|
|||
# sort the results
|
||||
return [[(k, r[k]), (k, None)] for k in sorted(r.keys())]
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
[
|
||||
|
|
|
@ -19,9 +19,10 @@
|
|||
|
||||
import os
|
||||
|
||||
from typing import Tuple
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from diffuse import utils
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.vcs.svn import Svn
|
||||
|
||||
|
||||
|
@ -41,14 +42,14 @@ class Svk(Svn):
|
|||
return s[0], s[4:]
|
||||
|
||||
@staticmethod
|
||||
def _getPreviousRevision(rev: str) -> str:
|
||||
def _getPreviousRevision(rev: Optional[str]) -> str:
|
||||
if rev is None:
|
||||
return 'HEAD'
|
||||
if rev.endswith('@'):
|
||||
return str(int(rev[:-1]) - 1) + '@'
|
||||
return str(int(rev) - 1)
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
relpath = utils.relpath(self.root, os.path.abspath(name)).replace(os.sep, '/')
|
||||
return utils.popenRead(
|
||||
self.root,
|
||||
|
|
|
@ -24,6 +24,7 @@ from gettext import gettext as _
|
|||
from typing import Optional, Tuple
|
||||
|
||||
from diffuse import utils
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.vcs.folder_set import FolderSet
|
||||
from diffuse.vcs.vcs_interface import VcsInterface
|
||||
|
||||
|
@ -32,7 +33,7 @@ from diffuse.vcs.vcs_interface import VcsInterface
|
|||
# SVK support subclasses from this
|
||||
class Svn(VcsInterface):
|
||||
def __init__(self, root: str):
|
||||
VcsInterface.__init__(self, root)
|
||||
super().__init__(root)
|
||||
self.url: Optional[str] = None
|
||||
|
||||
@staticmethod
|
||||
|
@ -54,13 +55,13 @@ class Svn(VcsInterface):
|
|||
return s[0], s[k:]
|
||||
|
||||
@staticmethod
|
||||
def _getPreviousRevision(rev: str) -> str:
|
||||
def _getPreviousRevision(rev: Optional[str]) -> str:
|
||||
if rev is None:
|
||||
return 'BASE'
|
||||
m = int(rev)
|
||||
return str(max(m > 1, 0))
|
||||
|
||||
def _getURL(self, prefs):
|
||||
def _getURL(self, prefs: Preferences) -> Optional[str]:
|
||||
if self.url is None:
|
||||
vcs, prefix = self._getVcs(), self._getURLPrefix()
|
||||
n = len(prefix)
|
||||
|
@ -71,7 +72,7 @@ class Svn(VcsInterface):
|
|||
break
|
||||
return self.url
|
||||
|
||||
def getFileTemplate(self, prefs, name):
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> VcsInterface.PathRevisionList:
|
||||
# FIXME: verify this
|
||||
# merge conflict
|
||||
escaped_name = utils.globEscape(name)
|
||||
|
@ -271,7 +272,7 @@ class Svn(VcsInterface):
|
|||
def getFolderTemplate(self, prefs, names):
|
||||
return self._getCommitTemplate(prefs, None, names)
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
vcs_bin = prefs.getString('svn_bin')
|
||||
if rev in ['BASE', 'COMMITTED', 'PREV']:
|
||||
return utils.popenRead(
|
||||
|
|
|
@ -17,22 +17,35 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
class VcsInterface:
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from diffuse.preferences import Preferences
|
||||
|
||||
|
||||
class VcsInterface(metaclass=ABCMeta):
|
||||
"""Interface for the VCSs."""
|
||||
|
||||
PathRevisionPair = Tuple[Optional[str], Optional[str]]
|
||||
PathRevisionList = List[PathRevisionPair]
|
||||
|
||||
def __init__(self, root: str):
|
||||
"""The object will initialized with the repository's root folder."""
|
||||
self.root = root
|
||||
|
||||
def getFileTemplate(self, prefs, name):
|
||||
@abstractmethod
|
||||
def getFileTemplate(self, prefs: Preferences, name: str) -> PathRevisionList:
|
||||
"""Indicates which revisions to display for a file when none were explicitly
|
||||
requested."""
|
||||
|
||||
@abstractmethod
|
||||
def getCommitTemplate(self, prefs, rev, names):
|
||||
"""Indicates which file revisions to display for a commit."""
|
||||
|
||||
@abstractmethod
|
||||
def getFolderTemplate(self, prefs, names):
|
||||
"""Indicates which file revisions to display for a set of folders."""
|
||||
|
||||
def getRevision(self, prefs, name, rev):
|
||||
@abstractmethod
|
||||
def getRevision(self, prefs: Preferences, name: str, rev: str) -> bytes:
|
||||
"""Returns the contents of the specified file revision"""
|
||||
|
|
|
@ -99,7 +99,7 @@ def _get_darcs_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
|||
def _get_git_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
||||
if 'GIT_DIR' in os.environ:
|
||||
try:
|
||||
ss: List[str] = utils.popenReadLines(
|
||||
lines: List[str] = utils.popenReadLines(
|
||||
path,
|
||||
[
|
||||
prefs.getString('git_bin'),
|
||||
|
@ -108,12 +108,12 @@ def _get_git_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
|||
],
|
||||
prefs,
|
||||
'git_bash')
|
||||
if len(ss) > 0:
|
||||
if len(lines) > 0:
|
||||
# be careful to handle trailing slashes
|
||||
dirs = path.split(os.sep)
|
||||
if dirs[-1] != '':
|
||||
dirs.append('')
|
||||
ss = utils.strip_eol(ss[0]).split('/')
|
||||
ss = utils.strip_eol(lines[0]).split('/')
|
||||
if ss[-1] != '':
|
||||
ss.append('')
|
||||
n = len(ss)
|
||||
|
@ -186,7 +186,8 @@ def _get_svk_repo(path: str, prefs: Preferences) -> Optional[VcsInterface]:
|
|||
# find working copies by parsing the config file
|
||||
with open(svkconfig, 'r', encoding='utf-8') as f:
|
||||
ss: List[str] = utils.readlines(f)
|
||||
projs, sep = [], os.sep
|
||||
projs: List[str] = []
|
||||
sep = os.sep
|
||||
# find the separator character
|
||||
for s in ss:
|
||||
if s.startswith(' sep: ') and len(s) > 7:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue