5 |
5 |
6 """ |
6 """ |
7 Module implementing a dialog to browse the log history. |
7 Module implementing a dialog to browse the log history. |
8 """ |
8 """ |
9 |
9 |
10 |
10 import re |
11 import os |
11 import os |
12 |
12 |
13 from PyQt5.QtCore import ( |
13 from PyQt5.QtCore import pyqtSlot, Qt, QTimer, QDate, QProcess, QPoint |
14 QTimer, QDate, QProcess, QRegExp, Qt, pyqtSlot, QPoint |
|
15 ) |
|
16 from PyQt5.QtWidgets import ( |
14 from PyQt5.QtWidgets import ( |
17 QHeaderView, QLineEdit, QWidget, QApplication, QDialogButtonBox, |
15 QHeaderView, QLineEdit, QWidget, QApplication, QDialogButtonBox, |
18 QTreeWidgetItem |
16 QTreeWidgetItem |
19 ) |
17 ) |
20 |
18 |
67 self.__process = E5OverrideCursorProcess() |
65 self.__process = E5OverrideCursorProcess() |
68 self.__process.finished.connect(self.__procFinished) |
66 self.__process.finished.connect(self.__procFinished) |
69 self.__process.readyReadStandardOutput.connect(self.__readStdout) |
67 self.__process.readyReadStandardOutput.connect(self.__readStdout) |
70 self.__process.readyReadStandardError.connect(self.__readStderr) |
68 self.__process.readyReadStandardError.connect(self.__readStderr) |
71 |
69 |
72 self.rx_sep1 = QRegExp('\\-+\\s*') |
70 self.rx_sep1 = re.compile('\\-+\\s*') |
73 self.rx_sep2 = QRegExp('=+\\s*') |
71 self.rx_sep2 = re.compile('=+\\s*') |
74 self.rx_rev1 = QRegExp( |
72 self.rx_rev1 = re.compile( |
75 r'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*') |
73 r'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*') |
76 # "rev" followed by one or more decimals followed by a colon followed |
74 # "rev" followed by one or more decimals followed by a colon followed |
77 # anything up to " | " (twice) followed by one or more decimals |
75 # anything up to " | " (twice) followed by one or more decimals |
78 # followed by anything |
76 # followed by anything |
79 self.rx_rev2 = QRegExp( |
77 self.rx_rev2 = re.compile( |
80 r'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*') |
78 r'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*') |
81 # "r" followed by one or more decimals followed by " | " followed |
79 # "r" followed by one or more decimals followed by " | " followed |
82 # anything up to " | " (twice) followed by one or more decimals |
80 # anything up to " | " (twice) followed by one or more decimals |
83 # followed by anything |
81 # followed by anything |
84 self.rx_flags1 = QRegExp( |
82 self.rx_flags1 = re.compile( |
85 r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""") |
83 r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""") |
86 # three blanks followed by A or D or M followed by path followed by |
84 # three blanks followed by A or D or M followed by path followed by |
87 # path copied from followed by copied from revision |
85 # path copied from followed by copied from revision |
88 self.rx_flags2 = QRegExp(' ([ADM]) (.*)\\s*') |
86 self.rx_flags2 = re.compile(' ([ADM]) (.*)\\s*') |
89 # three blanks followed by A or D or M followed by path |
87 # three blanks followed by A or D or M followed by path |
90 |
88 |
91 self.flags = { |
89 self.flags = { |
92 'A': self.tr('Added'), |
90 'A': self.tr('Added'), |
93 'D': self.tr('Deleted'), |
91 'D': self.tr('Deleted'), |
381 """ |
379 """ |
382 noEntries = 0 |
380 noEntries = 0 |
383 log = {"message": []} |
381 log = {"message": []} |
384 changedPaths = [] |
382 changedPaths = [] |
385 for s in self.buf: |
383 for s in self.buf: |
386 if self.rx_rev1.exactMatch(s): |
384 match = ( |
387 log["revision"] = self.rx_rev.cap(1) |
385 self.rx_rev1.fullmatch(s) or |
388 log["author"] = self.rx_rev.cap(2) |
386 self.rx_rev2.fullmatch(s) or |
389 log["date"] = self.rx_rev.cap(3) |
387 self.rx_flags1.fullmatch(s) or |
|
388 self.rx_flags2.fullmatch(s) or |
|
389 self.rx_sep1.fullmatch(s) or |
|
390 self.rx_sep2.fullmatch(s) |
|
391 ) |
|
392 if match is None: |
|
393 if s.strip().endswith(":") or not s.strip(): |
|
394 continue |
|
395 else: |
|
396 log["message"].append(s) |
|
397 elif match.re is self.rx_rev1: |
|
398 log["revision"] = match.group(1) |
|
399 log["author"] = match.group(2) |
|
400 log["date"] = match.group(3) |
390 # number of lines is ignored |
401 # number of lines is ignored |
391 elif self.rx_rev2.exactMatch(s): |
402 elif match.re is self.rx_rev2: |
392 log["revision"] = self.rx_rev2.cap(1) |
403 log["revision"] = match.group(1) |
393 log["author"] = self.rx_rev2.cap(2) |
404 log["author"] = match.group(2) |
394 log["date"] = " ".join(self.rx_rev2.cap(3).split()[:2]) |
405 log["date"] = " ".join(match.group(3).split()[:2]) |
395 # number of lines is ignored |
406 # number of lines is ignored |
396 elif self.rx_flags1.exactMatch(s): |
407 elif match.re is self.rx_flags1: |
397 changedPaths.append({ |
408 changedPaths.append({ |
398 "action": |
409 "action": match.group(1).strip(), |
399 self.rx_flags1.cap(1).strip(), |
410 "path": match.group(2).strip(), |
400 "path": |
411 "copyfrom_path": match.group(3).strip(), |
401 self.rx_flags1.cap(2).strip(), |
412 "copyfrom_revision": match.group(4).strip(), |
402 "copyfrom_path": |
|
403 self.rx_flags1.cap(3).strip(), |
|
404 "copyfrom_revision": |
|
405 self.rx_flags1.cap(4).strip(), |
|
406 }) |
413 }) |
407 elif self.rx_flags2.exactMatch(s): |
414 elif match.re is self.rx_flags2: |
408 changedPaths.append({ |
415 changedPaths.append({ |
409 "action": |
416 "action": match.group(1).strip(), |
410 self.rx_flags2.cap(1).strip(), |
417 "path": match.group(2).strip(), |
411 "path": |
|
412 self.rx_flags2.cap(2).strip(), |
|
413 "copyfrom_path": "", |
418 "copyfrom_path": "", |
414 "copyfrom_revision": "", |
419 "copyfrom_revision": "", |
415 }) |
420 }) |
416 elif self.rx_sep1.exactMatch(s) or self.rx_sep2.exactMatch(s): |
421 elif ( |
|
422 match.re is self.rx_sep1 or |
|
423 match.re is self.rx_sep2 |
|
424 ): |
417 if len(log) > 1: |
425 if len(log) > 1: |
418 self.__generateLogItem( |
426 self.__generateLogItem( |
419 log["author"], log["date"], log["message"], |
427 log["author"], log["date"], log["message"], |
420 log["revision"], changedPaths) |
428 log["revision"], changedPaths) |
421 dt = QDate.fromString(log["date"], Qt.ISODate) |
429 dt = QDate.fromString(log["date"], Qt.ISODate) |
655 from_ = self.fromDate.date().toString("yyyy-MM-dd") |
658 from_ = self.fromDate.date().toString("yyyy-MM-dd") |
656 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") |
659 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") |
657 txt = self.fieldCombo.currentText() |
660 txt = self.fieldCombo.currentText() |
658 if txt == self.tr("Author"): |
661 if txt == self.tr("Author"): |
659 fieldIndex = 1 |
662 fieldIndex = 1 |
660 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive) |
663 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
661 elif txt == self.tr("Revision"): |
664 elif txt == self.tr("Revision"): |
662 fieldIndex = 0 |
665 fieldIndex = 0 |
663 txt = self.rxEdit.text() |
666 txt = self.rxEdit.text() |
664 if txt.startswith("^"): |
667 if txt.startswith("^"): |
665 searchRx = QRegExp( |
668 searchRx = re.compile( |
666 r"^\s*{0}".format(txt[1:]), Qt.CaseInsensitive) |
669 r"^\s*{0}".format(txt[1:]), re.IGNORECASE) |
667 else: |
670 else: |
668 searchRx = QRegExp(txt, Qt.CaseInsensitive) |
671 searchRx = re.compile(txt, re.IGNORECASE) |
669 else: |
672 else: |
670 fieldIndex = 3 |
673 fieldIndex = 3 |
671 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive) |
674 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
672 |
675 |
673 currentItem = self.logTree.currentItem() |
676 currentItem = self.logTree.currentItem() |
674 for topIndex in range(self.logTree.topLevelItemCount()): |
677 for topIndex in range(self.logTree.topLevelItemCount()): |
675 topItem = self.logTree.topLevelItem(topIndex) |
678 topItem = self.logTree.topLevelItem(topIndex) |
676 if ( |
679 if ( |
677 topItem.text(2) <= to_ and |
680 topItem.text(2) <= to_ and |
678 topItem.text(2) >= from_ and |
681 topItem.text(2) >= from_ and |
679 searchRx.indexIn(topItem.text(fieldIndex)) > -1 |
682 searchRx.match(topItem.text(fieldIndex)) is not None |
680 ): |
683 ): |
681 topItem.setHidden(False) |
684 topItem.setHidden(False) |
682 if topItem is currentItem: |
685 if topItem is currentItem: |
683 self.on_logTree_currentItemChanged(topItem, None) |
686 self.on_logTree_currentItemChanged(topItem, None) |
684 else: |
687 else: |