mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Refactor to support multiple diffs in the same view
This commit is contained in:
parent
2eccbae35f
commit
20295489cd
@ -14,7 +14,7 @@ from difflib import SequenceMatcher
|
||||
from PyQt4.Qt import (
|
||||
QSplitter, QApplication, QPlainTextDocumentLayout, QTextDocument,
|
||||
QTextCursor, QTextCharFormat, Qt, QRect, QPainter, QPalette, QPen,
|
||||
QBrush, QColor, QTextLayout)
|
||||
QBrush, QColor, QTextLayout, QCursor)
|
||||
|
||||
from calibre.gui2.tweak_book import tprefs
|
||||
from calibre.gui2.tweak_book.editor.text import PlainTextEdit, get_highlighter, default_font_family, LineNumbers
|
||||
@ -23,6 +23,14 @@ from calibre.utils.diff import get_sequence_matcher
|
||||
|
||||
Change = namedtuple('Change', 'ltop lbot rtop rbot kind')
|
||||
|
||||
class BusyCursor(object):
|
||||
|
||||
def __enter__(self):
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
|
||||
def __exit__(self, *args):
|
||||
QApplication.restoreOverrideCursor()
|
||||
|
||||
def get_theme():
|
||||
theme = THEMES.get(tprefs['editor_theme'], None)
|
||||
if theme is None:
|
||||
@ -222,36 +230,52 @@ class TextDiffView(QSplitter):
|
||||
self.left, self.right = TextBrowser(parent=self), TextBrowser(right=True, parent=self)
|
||||
self.addWidget(self.left), self.addWidget(self.right)
|
||||
self.split_words = re.compile(r"\w+|\W", re.UNICODE)
|
||||
self.clear()
|
||||
|
||||
def __call__(self, left_text, right_text, context=None, syntax=None):
|
||||
def clear(self):
|
||||
self.left.clear(), self.right.clear()
|
||||
self.changes = []
|
||||
|
||||
def finalize(self):
|
||||
for v in (self.left, self.right):
|
||||
c = v.textCursor()
|
||||
c.movePosition(c.Start)
|
||||
v.setTextCursor(c)
|
||||
self.update()
|
||||
|
||||
def add_diff(self, left_text, right_text, context=None, syntax=None):
|
||||
is_text = isinstance(left_text, type('')) or isinstance(right_text, type(''))
|
||||
with BusyCursor():
|
||||
if is_text:
|
||||
self.add_text_diff(left_text, right_text, context, syntax)
|
||||
|
||||
def add_text_diff(self, left_text, right_text, context, syntax):
|
||||
left_text = unicodedata.normalize('NFC', left_text)
|
||||
right_text = unicodedata.normalize('NFC', right_text)
|
||||
left_lines = self.left_lines = left_text.splitlines()
|
||||
right_lines = self.right_lines = right_text.splitlines()
|
||||
self.left_highlight, self.right_highlight = Highlight(self, left_text, syntax), Highlight(self, right_text, syntax)
|
||||
self.context = context
|
||||
|
||||
self.cruncher = get_sequence_matcher()(None, left_lines, right_lines)
|
||||
self.do_layout(context)
|
||||
cruncher = get_sequence_matcher()(None, left_lines, right_lines)
|
||||
|
||||
def refresh(self):
|
||||
self.do_layout(self.context)
|
||||
|
||||
def do_layout(self, context=None):
|
||||
self.left.clear(), self.right.clear()
|
||||
left_highlight, right_highlight = Highlight(self, left_text, syntax), Highlight(self, right_text, syntax)
|
||||
cl, cr = self.left_cursor, self.right_cursor = self.left.textCursor(), self.right.textCursor()
|
||||
cl.beginEditBlock(), cr.beginEditBlock()
|
||||
cl.movePosition(cl.End), cr.movePosition(cr.End)
|
||||
self.left_insert = partial(self.do_insert, cl, left_highlight, self.left.line_number_map)
|
||||
self.right_insert = partial(self.do_insert, cr, right_highlight, self.right.line_number_map)
|
||||
|
||||
ochanges = []
|
||||
self.changes = []
|
||||
self.left_insert = partial(self.do_insert, cl, self.left_highlight, self.left.line_number_map)
|
||||
self.right_insert = partial(self.do_insert, cr, self.right_highlight, self.right.line_number_map)
|
||||
|
||||
if context is None:
|
||||
for tag, alo, ahi, blo, bhi in self.cruncher.get_opcodes():
|
||||
for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
|
||||
getattr(self, tag)(alo, ahi, blo, bhi)
|
||||
QApplication.processEvents()
|
||||
else:
|
||||
for group in self.cruncher.get_grouped_opcodes(context):
|
||||
for group in cruncher.get_grouped_opcodes(context):
|
||||
for tag, alo, ahi, blo, bhi in group:
|
||||
getattr(self, tag)(alo, ahi, blo, bhi)
|
||||
QApplication.processEvents()
|
||||
cl.insertBlock(), cr.insertBlock()
|
||||
self.changes.append(Change(
|
||||
ltop=cl.block().blockNumber()-1, lbot=cl.block().blockNumber(),
|
||||
@ -259,13 +283,11 @@ class TextDiffView(QSplitter):
|
||||
self.left.line_number_map[self.changes[-1].ltop] = '-'
|
||||
self.right.line_number_map[self.changes[-1].rtop] = '-'
|
||||
cl.endEditBlock(), cr.endEditBlock()
|
||||
self.equalize_block_counts()
|
||||
del self.left_lines
|
||||
del self.right_lines
|
||||
|
||||
for v in (self.left, self.right):
|
||||
c = v.textCursor()
|
||||
c.movePosition(c.Start)
|
||||
v.setTextCursor(c)
|
||||
del self.left_insert
|
||||
del self.right_insert
|
||||
|
||||
self.coalesce_changes()
|
||||
|
||||
@ -274,7 +296,13 @@ class TextDiffView(QSplitter):
|
||||
self.left.changes.append((ltop, lbot, kind))
|
||||
self.right.changes.append((rtop, rbot, kind))
|
||||
|
||||
self.update()
|
||||
self.changes = ochanges + self.changes
|
||||
|
||||
def equalize_block_counts(self):
|
||||
l, r = self.left.blockCount(), self.right.blockCount()
|
||||
c = (self.left if l < r else self.right).textCursor()
|
||||
c.movePosition(c.End)
|
||||
c.insertText('\n' * (abs(l - r)))
|
||||
|
||||
def coalesce_changes(self):
|
||||
'Merge neighboring changes of the same kind, if any'
|
||||
@ -429,7 +457,8 @@ if __name__ == '__main__':
|
||||
raw2 = open(sys.argv[-1], 'rb').read().decode('utf-8')
|
||||
w = TextDiffView()
|
||||
w.show()
|
||||
w(raw1, raw2, syntax='html', context=3)
|
||||
w.add_diff(raw1, raw2, syntax='html', context=3)
|
||||
w.finalize()
|
||||
app.exec_()
|
||||
|
||||
# TODO: Add diff colors for other color schemes
|
||||
|
Loading…
x
Reference in New Issue
Block a user