diff options
author | Matias Linares <matias@deprecated.org> | 2025-03-05 17:48:23 -0300 |
---|---|---|
committer | Matias Linares <matias@deprecated.org> | 2025-03-05 17:48:23 -0300 |
commit | 99a8e02c5fd26be5ac80efa35c33b550df9c0ee9 (patch) | |
tree | 70faa7b35a385d6a096d023584ee44e890a02b31 | |
parent | f2305655d86728e3fa8fb3214dd9acf10fce6372 (diff) | |
download | kodereviewer-99a8e02c5fd26be5ac80efa35c33b550df9c0ee9.tar.gz |
Add DiffFileModel
-rw-r--r-- | kodereviewer/app.py | 3 | ||||
-rw-r--r-- | kodereviewer/models/file.py | 142 | ||||
-rw-r--r-- | kodereviewer/qml/FilesChangedPage.qml | 5 |
3 files changed, 147 insertions, 3 deletions
diff --git a/kodereviewer/app.py b/kodereviewer/app.py index e188923..fbcc7b8 100644 --- a/kodereviewer/app.py +++ b/kodereviewer/app.py @@ -13,7 +13,7 @@ from PySide6.QtQuickControls2 import QQuickStyle from rich.logging import RichHandler from kodereviewer.data import PullRequest -from kodereviewer.models.file import FileModel +from kodereviewer.models.file import DiffFileModel, FileModel from kodereviewer.network_manager import NetworkManager from kodereviewer.models import ( CommentModel, TreeFileModel, LabelModel, LineModel, PullRequestModel, @@ -55,6 +55,7 @@ def main(): qmlRegisterType(CommentModel, "org.deprecated.kodereviewer", 1, 0, "CommentModel") qmlRegisterType(FileModel, "org.deprecated.kodereviewer", 1, 0, "FileModel") + qmlRegisterType(DiffFileModel, "org.deprecated.kodereviewer", 1, 0, "DiffFileModel") qmlRegisterType(TreeFileModel, "org.deprecated.kodereviewer", 1, 0, "TreeFileModel") qmlRegisterType(LabelModel, "org.deprecated.kodereviewer", 1, 0, "LabelModel") qmlRegisterType(LineModel, "org.deprecated.kodereviewer", 1, 0, "LineModel") diff --git a/kodereviewer/models/file.py b/kodereviewer/models/file.py index 2e00755..ce47bcc 100644 --- a/kodereviewer/models/file.py +++ b/kodereviewer/models/file.py @@ -1,9 +1,10 @@ +from collections.abc import Sequence import logging from copy import copy from dataclasses import dataclass, field from enum import IntEnum, auto from pathlib import Path -from typing import Any, Optional, Self +from typing import Any, Optional, Self, override from PySide6.QtCore import ( QAbstractListModel, QAbstractItemModel, QByteArray, QModelIndex, QObject, @@ -11,6 +12,7 @@ from PySide6.QtCore import ( ) from PySide6.QtQml import QmlElement +from kodereviewer.diff_parser import GithubDiffParser from kodereviewer.project import Project from kodereviewer.data import ChangedFile, Label, PullRequest @@ -130,7 +132,7 @@ class TreeFileModel(QAbstractItemModel): self.endResetModel() def index(self, row: int, column: int, - parent: QModelIndex = QModelIndex()) -> QModelIndex: + parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> QModelIndex: """Returns the index of the item in the model specified by the given row, column and parent index.""" if not self.hasIndex(row, column, parent): return QModelIndex() @@ -204,6 +206,142 @@ class TreeFileModel(QAbstractItemModel): } +@dataclass +class DiffFileItem: + text: str + parent: Self | None = None + children: list[Self] = field(default_factory=list) + + def append(self, child: Self) -> None: + self.children.append(child) + + def child(self, row: int) -> Self | None: + if row >= 0and row < len(self.children): + return self.children[row] + return None + + def row(self) -> int: + if self.parent is None: + return 0 + for i, child in enumerate(self.parent.children): + if self is child: + return i + raise Exception('Should not happen!') + + def column_count(self) -> int: + """Only text""" + return 1 + + def __str__(self) -> str: + return f'DiffFileItem: "self.text"' + + +@QmlElement +class DiffFileModel(QAbstractItemModel): + items: Sequence[DiffFileItem] + _diff: str + + class Roles(IntEnum): + Text = Qt.ItemDataRole.UserRole + 1 + + def __init__(self): + super().__init__() + self.root_node = DiffFileItem(text='') + self._diff = '' + + def parse_file(self, diff: str): + logger.info('Parsing with github diff parser') + parser = GithubDiffParser(diff) + for hunk in parser.hunks(): + logger.info('loading hunk with context %s', hunk.context) + context = hunk.context + text = '\n'.join(hunk.lines) + + context_item = DiffFileItem(text=context, parent=self.root_node) + self.root_node.append(context_item) + diff_item = DiffFileItem(text=text, parent=context_item) + context_item.append(diff_item) + + def get_diff(self) -> str: + return self._diff + + def set_diff(self, diff: str) -> None: + if not diff: + return + + if diff == self._diff: + return + + self.beginResetModel() + self._diff = diff + self.parse_file(self._diff) + self.endResetModel() + + diffChanged = Signal() + diff = Property(str, fget=get_diff, fset=set_diff, notify=diffChanged) + + def index( + self, row: int, column: int, + parent: QModelIndex | QPersistentModelIndex = QModelIndex() + ) -> QModelIndex: + if not self.hasIndex(row, column, parent): + return QModelIndex() + + item: DiffFileItem + if parent.isValid(): + item = parent.internalPointer() + else: + item = self.root_node + + if item.child(row): + return self.createIndex(row, column, item.child(row)) + + return QModelIndex() + + def parent(self, index: QModelIndex) -> QModelIndex: + if not index.isValid(): + return QModelIndex() + + item: DiffFileItem = index.internalPointer() + parent: DiffFileItem | None = item.parent + + if parent is None or parent == self.root_node: + return QModelIndex() + + return self.createIndex(parent.row(), 0, parent) + + + def rowCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> int: + node: DiffFileItem + if parent.isValid(): + node = parent.internalPointer() + else: + node = self.root_node + + return len(node.children) + + def columnCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> int: + return 1 + + def data( + self, index: QModelIndex | QPersistentModelIndex, + role: int = Qt.ItemDataRole.DisplayRole + ) -> Any: + if not index.isValid(): + return '' + item: DiffFileItem = index.internalPointer() + if role == Qt.ItemDataRole.DisplayRole: + return item.text + if role == self.Roles.Text: + return item.text + + return None + + def roleNames(self) -> dict[int, QByteArray]: + return { + self.Roles.Text: QByteArray(b'text'), + } + @QmlElement class FileModel(QAbstractListModel): diff --git a/kodereviewer/qml/FilesChangedPage.qml b/kodereviewer/qml/FilesChangedPage.qml index 25c1164..f1ee031 100644 --- a/kodereviewer/qml/FilesChangedPage.qml +++ b/kodereviewer/qml/FilesChangedPage.qml @@ -27,6 +27,11 @@ Kirigami.ScrollablePage { property string currentFile: "" property string currentText: "" + DiffFileModel { + id: diffFileModel + diff: root.currentText + } + onPullRequestChanged: { root.currentFile = "" root.currentText = "" |