vibefpmgui.file_diff

A file's diff with a line over it saying what's compared ("staged → working tree", "3f9db510 → 930ec19c"): the Git panel's Diff page and its History's. The two versions are read, and compared, on a pool thread; a file chosen meanwhile wins (the older answer is dropped).

 1"""A file's diff with a line over it saying what's compared ("staged →
 2working tree", "3f9db510 → 930ec19c"): the Git panel's Diff page and its
 3History's. The two versions are read, and compared, on a pool thread; a
 4file chosen meanwhile wins (the older answer is dropped).
 5"""
 6
 7from __future__ import annotations
 8
 9from collections.abc import Callable, Hashable
10
11from qtpy.QtCore import Signal
12from qtpy.QtGui import QFont
13from qtpy.QtWidgets import QVBoxLayout, QWidget
14
15from .background import call_in_background
16from .diff_view import DiffView, compare
17from .git_status import BINARY
18from .widgets import StatusLabel
19
20# What load() reads: (the older text, the newer one, what's compared). A text
21# is None where there's no such version (added, deleted), BINARY where it
22# isn't text. It raises OSError, or RuntimeError (git_status.GitError), if
23# it can't be read.
24Versions = tuple[str | None, str | None, str]
25
26
27class FileDiff(QWidget):
28    open_requested = Signal(int)  # a line of the newer version (1-based), double-clicked
29
30    def __init__(self, parent=None):
31        super().__init__(parent)
32        self.title = StatusLabel()
33        self.diff = DiffView()
34        self.diff.open_requested.connect(self.open_requested)
35        self._loading: Hashable | None = None  # (what's being read: see load)
36        layout = QVBoxLayout(self)
37        layout.setContentsMargins(0, 0, 0, 0)
38        layout.setSpacing(4)
39        layout.addWidget(self.title)
40        layout.addWidget(self.diff, 1)
41
42    def load(self, key: Hashable, path: str, read: Callable[[], Versions]):
43        """Show ``path``'s diff, its versions from ``read()`` (on a pool
44        thread); ``key`` tells this file apart from the next one chosen."""
45        self._loading = key
46        self.title.setText(f"<b>{path}</b> · reading…")
47
48        def work():
49            try:
50                old, new, compared = read()
51            except (OSError, RuntimeError) as error:
52                return str(error) or type(error).__name__
53            # (Compared here too: a third of a second for a big file, which the window shouldn't wait for.)
54            return compared, None if BINARY in (old, new) else compare(old, new)
55
56        call_in_background(work, lambda result: self._read(key, path, result), self)
57
58    def _read(self, key: Hashable, path: str, result):
59        if key != self._loading:
60            return  # (another file chosen meanwhile)
61        if isinstance(result, str):
62            self.title.setText(f"<b>{path}</b> · couldn't read it: {result}")
63            self.diff.clear()
64            return
65        compared, rows = result
66        if rows is None:
67            self.title.setText(f"<b>{path}</b> · binary or very big: no diff shown")
68            self.diff.clear()
69            return
70        self.title.setText(f"<b>{path}</b> · {compared}")
71        self.diff.show_rows(rows, path)
72
73    def clear(self):
74        self._loading = None
75        self.title.setText("")
76        self.diff.clear()
77
78    def set_unified(self, on: bool):
79        self.diff.set_unified(on)
80
81    def set_font(self, font: QFont):
82        self.diff.set_font(font)
83
84    def refresh_style(self, muted: str):
85        """The colors: the title's (muted), the diff's (the syntax style's)."""
86        self.title.setStyleSheet(f"color: {muted};")
87        self.diff.refresh_style()
Versions = tuple[str | None, str | None, str]
class FileDiff(PySide6.QtWidgets.QWidget):
28class FileDiff(QWidget):
29    open_requested = Signal(int)  # a line of the newer version (1-based), double-clicked
30
31    def __init__(self, parent=None):
32        super().__init__(parent)
33        self.title = StatusLabel()
34        self.diff = DiffView()
35        self.diff.open_requested.connect(self.open_requested)
36        self._loading: Hashable | None = None  # (what's being read: see load)
37        layout = QVBoxLayout(self)
38        layout.setContentsMargins(0, 0, 0, 0)
39        layout.setSpacing(4)
40        layout.addWidget(self.title)
41        layout.addWidget(self.diff, 1)
42
43    def load(self, key: Hashable, path: str, read: Callable[[], Versions]):
44        """Show ``path``'s diff, its versions from ``read()`` (on a pool
45        thread); ``key`` tells this file apart from the next one chosen."""
46        self._loading = key
47        self.title.setText(f"<b>{path}</b> · reading…")
48
49        def work():
50            try:
51                old, new, compared = read()
52            except (OSError, RuntimeError) as error:
53                return str(error) or type(error).__name__
54            # (Compared here too: a third of a second for a big file, which the window shouldn't wait for.)
55            return compared, None if BINARY in (old, new) else compare(old, new)
56
57        call_in_background(work, lambda result: self._read(key, path, result), self)
58
59    def _read(self, key: Hashable, path: str, result):
60        if key != self._loading:
61            return  # (another file chosen meanwhile)
62        if isinstance(result, str):
63            self.title.setText(f"<b>{path}</b> · couldn't read it: {result}")
64            self.diff.clear()
65            return
66        compared, rows = result
67        if rows is None:
68            self.title.setText(f"<b>{path}</b> · binary or very big: no diff shown")
69            self.diff.clear()
70            return
71        self.title.setText(f"<b>{path}</b> · {compared}")
72        self.diff.show_rows(rows, path)
73
74    def clear(self):
75        self._loading = None
76        self.title.setText("")
77        self.diff.clear()
78
79    def set_unified(self, on: bool):
80        self.diff.set_unified(on)
81
82    def set_font(self, font: QFont):
83        self.diff.set_font(font)
84
85    def refresh_style(self, muted: str):
86        """The colors: the title's (muted), the diff's (the syntax style's)."""
87        self.title.setStyleSheet(f"color: {muted};")
88        self.diff.refresh_style()

QWidget(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, modal: bool | None = None, windowModality: PySide6.QtCore.Qt.WindowModality | None = None, enabled: bool | None = None, geometry: PySide6.QtCore.QRect | None = None, frameGeometry: PySide6.QtCore.QRect | None = None, normalGeometry: PySide6.QtCore.QRect | None = None, x: int | None = None, y: int | None = None, pos: PySide6.QtCore.QPoint | None = None, frameSize: PySide6.QtCore.QSize | None = None, size: PySide6.QtCore.QSize | None = None, width: int | None = None, height: int | None = None, rect: PySide6.QtCore.QRect | None = None, childrenRect: PySide6.QtCore.QRect | None = None, childrenRegion: PySide6.QtGui.QRegion | None = None, sizePolicy: PySide6.QtWidgets.QSizePolicy | None = None, minimumSize: PySide6.QtCore.QSize | None = None, maximumSize: PySide6.QtCore.QSize | None = None, minimumWidth: int | None = None, minimumHeight: int | None = None, maximumWidth: int | None = None, maximumHeight: int | None = None, sizeIncrement: PySide6.QtCore.QSize | None = None, baseSize: PySide6.QtCore.QSize | None = None, palette: PySide6.QtGui.QPalette | None = None, font: PySide6.QtGui.QFont | None = None, cursor: PySide6.QtGui.QCursor | None = None, mouseTracking: bool | None = None, tabletTracking: bool | None = None, isActiveWindow: bool | None = None, focusPolicy: PySide6.QtCore.Qt.FocusPolicy | None = None, focus: bool | None = None, contextMenuPolicy: PySide6.QtCore.Qt.ContextMenuPolicy | None = None, updatesEnabled: bool | None = None, visible: bool | None = None, minimized: bool | None = None, maximized: bool | None = None, fullScreen: bool | None = None, sizeHint: PySide6.QtCore.QSize | None = None, minimumSizeHint: PySide6.QtCore.QSize | None = None, acceptDrops: bool | None = None, windowTitle: str | None = None, windowIcon: PySide6.QtGui.QIcon | None = None, windowIconText: str | None = None, windowOpacity: float | None = None, windowModified: bool | None = None, toolTip: str | None = None, toolTipDuration: int | None = None, statusTip: str | None = None, whatsThis: str | None = None, accessibleName: str | None = None, accessibleDescription: str | None = None, accessibleIdentifier: str | None = None, layoutDirection: PySide6.QtCore.Qt.LayoutDirection | None = None, autoFillBackground: bool | None = None, styleSheet: str | None = None, locale: PySide6.QtCore.QLocale | None = None, windowFilePath: str | None = None, inputMethodHints: PySide6.QtCore.Qt.InputMethodHint | None = None) -> None

FileDiff(parent=None)
31    def __init__(self, parent=None):
32        super().__init__(parent)
33        self.title = StatusLabel()
34        self.diff = DiffView()
35        self.diff.open_requested.connect(self.open_requested)
36        self._loading: Hashable | None = None  # (what's being read: see load)
37        layout = QVBoxLayout(self)
38        layout.setContentsMargins(0, 0, 0, 0)
39        layout.setSpacing(4)
40        layout.addWidget(self.title)
41        layout.addWidget(self.diff, 1)

__init__(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, modal: bool | None = None, windowModality: PySide6.QtCore.Qt.WindowModality | None = None, enabled: bool | None = None, geometry: PySide6.QtCore.QRect | None = None, frameGeometry: PySide6.QtCore.QRect | None = None, normalGeometry: PySide6.QtCore.QRect | None = None, x: int | None = None, y: int | None = None, pos: PySide6.QtCore.QPoint | None = None, frameSize: PySide6.QtCore.QSize | None = None, size: PySide6.QtCore.QSize | None = None, width: int | None = None, height: int | None = None, rect: PySide6.QtCore.QRect | None = None, childrenRect: PySide6.QtCore.QRect | None = None, childrenRegion: PySide6.QtGui.QRegion | None = None, sizePolicy: PySide6.QtWidgets.QSizePolicy | None = None, minimumSize: PySide6.QtCore.QSize | None = None, maximumSize: PySide6.QtCore.QSize | None = None, minimumWidth: int | None = None, minimumHeight: int | None = None, maximumWidth: int | None = None, maximumHeight: int | None = None, sizeIncrement: PySide6.QtCore.QSize | None = None, baseSize: PySide6.QtCore.QSize | None = None, palette: PySide6.QtGui.QPalette | None = None, font: PySide6.QtGui.QFont | None = None, cursor: PySide6.QtGui.QCursor | None = None, mouseTracking: bool | None = None, tabletTracking: bool | None = None, isActiveWindow: bool | None = None, focusPolicy: PySide6.QtCore.Qt.FocusPolicy | None = None, focus: bool | None = None, contextMenuPolicy: PySide6.QtCore.Qt.ContextMenuPolicy | None = None, updatesEnabled: bool | None = None, visible: bool | None = None, minimized: bool | None = None, maximized: bool | None = None, fullScreen: bool | None = None, sizeHint: PySide6.QtCore.QSize | None = None, minimumSizeHint: PySide6.QtCore.QSize | None = None, acceptDrops: bool | None = None, windowTitle: str | None = None, windowIcon: PySide6.QtGui.QIcon | None = None, windowIconText: str | None = None, windowOpacity: float | None = None, windowModified: bool | None = None, toolTip: str | None = None, toolTipDuration: int | None = None, statusTip: str | None = None, whatsThis: str | None = None, accessibleName: str | None = None, accessibleDescription: str | None = None, accessibleIdentifier: str | None = None, layoutDirection: PySide6.QtCore.Qt.LayoutDirection | None = None, autoFillBackground: bool | None = None, styleSheet: str | None = None, locale: PySide6.QtCore.QLocale | None = None, windowFilePath: str | None = None, inputMethodHints: PySide6.QtCore.Qt.InputMethodHint | None = None) -> None

Initialize self. See help(type(self)) for accurate signature.

def open_requested(unknown):

The type of the None singleton.

title
diff
def load( self, key: Hashable, path: str, read: Callable[[], tuple[str | None, str | None, str]]):
43    def load(self, key: Hashable, path: str, read: Callable[[], Versions]):
44        """Show ``path``'s diff, its versions from ``read()`` (on a pool
45        thread); ``key`` tells this file apart from the next one chosen."""
46        self._loading = key
47        self.title.setText(f"<b>{path}</b> · reading…")
48
49        def work():
50            try:
51                old, new, compared = read()
52            except (OSError, RuntimeError) as error:
53                return str(error) or type(error).__name__
54            # (Compared here too: a third of a second for a big file, which the window shouldn't wait for.)
55            return compared, None if BINARY in (old, new) else compare(old, new)
56
57        call_in_background(work, lambda result: self._read(key, path, result), self)

Show path's diff, its versions from read() (on a pool thread); key tells this file apart from the next one chosen.

def clear(self):
74    def clear(self):
75        self._loading = None
76        self.title.setText("")
77        self.diff.clear()
def set_unified(self, on: bool):
79    def set_unified(self, on: bool):
80        self.diff.set_unified(on)
def set_font(self, font: PySide6.QtGui.QFont):
82    def set_font(self, font: QFont):
83        self.diff.set_font(font)
def refresh_style(self, muted: str):
85    def refresh_style(self, muted: str):
86        """The colors: the title's (muted), the diff's (the syntax style's)."""
87        self.title.setStyleSheet(f"color: {muted};")
88        self.diff.refresh_style()

The colors: the title's (muted), the diff's (the syntax style's).

staticMetaObject = PySide6.QtCore.QMetaObject("FileDiff" inherits "QWidget": Methods: #33 type=Signal, signature=open_requested(int), parameters=int )