mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Syntax highlighting for the diff view
This commit is contained in:
parent
fff1a25981
commit
76861d9144
@ -9,19 +9,76 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import sys
|
import sys
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (QSplitter, QApplication)
|
from PyQt4.Qt import (QSplitter, QApplication, QPlainTextDocumentLayout, QTextDocument, QTextCursor, QTextCharFormat)
|
||||||
|
|
||||||
from calibre.gui2.tweak_book.editor.text import PlainTextEdit
|
from calibre.gui2.tweak_book import tprefs
|
||||||
|
from calibre.gui2.tweak_book.editor.text import PlainTextEdit, get_highlighter, default_font_family
|
||||||
|
from calibre.gui2.tweak_book.editor.themes import THEMES, default_theme, theme_color
|
||||||
from calibre.utils.diff import get_sequence_matcher
|
from calibre.utils.diff import get_sequence_matcher
|
||||||
|
|
||||||
|
def get_theme():
|
||||||
|
theme = THEMES.get(tprefs['editor_theme'], None)
|
||||||
|
if theme is None:
|
||||||
|
theme = THEMES[default_theme()]
|
||||||
|
return theme
|
||||||
|
|
||||||
class TextBrowser(PlainTextEdit):
|
class TextBrowser(PlainTextEdit):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
PlainTextEdit.__init__(self, parent)
|
PlainTextEdit.__init__(self, parent)
|
||||||
self.setReadOnly(True)
|
self.setReadOnly(True)
|
||||||
self.setLineWrapMode(self.NoWrap)
|
w = self.fontMetrics()
|
||||||
|
self.space_width = w.width(' ')
|
||||||
|
self.setLineWrapMode(self.WidgetWidth if tprefs['editor_line_wrap'] else self.NoWrap)
|
||||||
|
self.setTabStopWidth(tprefs['editor_tab_stop_width'] * self.space_width)
|
||||||
|
font = self.font()
|
||||||
|
ff = tprefs['editor_font_family']
|
||||||
|
if ff is None:
|
||||||
|
ff = default_font_family()
|
||||||
|
font.setFamily(ff)
|
||||||
|
font.setPointSize(tprefs['editor_font_size'])
|
||||||
|
self.setFont(font)
|
||||||
|
theme = get_theme()
|
||||||
|
pal = self.palette()
|
||||||
|
pal.setColor(pal.Base, theme_color(theme, 'Normal', 'bg'))
|
||||||
|
pal.setColor(pal.AlternateBase, theme_color(theme, 'CursorLine', 'bg'))
|
||||||
|
pal.setColor(pal.Text, theme_color(theme, 'Normal', 'fg'))
|
||||||
|
pal.setColor(pal.Highlight, theme_color(theme, 'Visual', 'bg'))
|
||||||
|
pal.setColor(pal.HighlightedText, theme_color(theme, 'Visual', 'fg'))
|
||||||
|
self.setPalette(pal)
|
||||||
|
|
||||||
class DiffView(QSplitter):
|
class Highlight(QTextDocument):
|
||||||
|
|
||||||
|
def __init__(self, parent, text, syntax):
|
||||||
|
QTextDocument.__init__(self, parent)
|
||||||
|
self.l = QPlainTextDocumentLayout(self)
|
||||||
|
self.setDocumentLayout(self.l)
|
||||||
|
self.highlighter = get_highlighter(syntax)(self)
|
||||||
|
self.highlighter.apply_theme(get_theme())
|
||||||
|
self.highlighter.setDocument(self)
|
||||||
|
self.setPlainText(text)
|
||||||
|
|
||||||
|
def copy_lines(self, lo, hi, cursor):
|
||||||
|
''' Copy specified lines from the syntax highlighted buffer into the
|
||||||
|
destination cursor, preserving all formatting created by the syntax
|
||||||
|
highlighter. '''
|
||||||
|
num = hi - lo
|
||||||
|
if num > 0:
|
||||||
|
block = self.findBlockByNumber(lo + 1)
|
||||||
|
while num > 0:
|
||||||
|
num -= 1
|
||||||
|
cursor.insertText(block.text())
|
||||||
|
dest_block = cursor.block()
|
||||||
|
c = QTextCursor(dest_block)
|
||||||
|
for af in block.layout().additionalFormats():
|
||||||
|
start = dest_block.position() + af.start
|
||||||
|
c.setPosition(start), c.setPosition(start + af.length, c.KeepAnchor)
|
||||||
|
c.setCharFormat(af.format)
|
||||||
|
cursor.insertBlock()
|
||||||
|
cursor.setCharFormat(QTextCharFormat())
|
||||||
|
block = block.next()
|
||||||
|
|
||||||
|
class TextDiffView(QSplitter):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QSplitter.__init__(self, parent)
|
QSplitter.__init__(self, parent)
|
||||||
@ -32,10 +89,11 @@ class DiffView(QSplitter):
|
|||||||
def __call__(self, left_text, right_text, context=None, syntax=None):
|
def __call__(self, left_text, right_text, context=None, syntax=None):
|
||||||
left_lines = left_text.splitlines()
|
left_lines = left_text.splitlines()
|
||||||
right_lines = right_text.splitlines()
|
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.context = context
|
||||||
|
|
||||||
self.left_insert = partial(self.do_insert, self.left.textCursor(), left_lines)
|
self.left_insert = partial(self.do_insert, self.left.textCursor(), self.left_highlight)
|
||||||
self.right_insert = partial(self.do_insert, self.right.textCursor(), right_lines)
|
self.right_insert = partial(self.do_insert, self.right.textCursor(), self.right_highlight)
|
||||||
self.cruncher = get_sequence_matcher()(None, left_lines, right_lines)
|
self.cruncher = get_sequence_matcher()(None, left_lines, right_lines)
|
||||||
self.do_layout(context)
|
self.do_layout(context)
|
||||||
|
|
||||||
@ -44,19 +102,22 @@ class DiffView(QSplitter):
|
|||||||
|
|
||||||
def do_layout(self, context=None):
|
def do_layout(self, context=None):
|
||||||
self.left.clear(), self.right.clear()
|
self.left.clear(), self.right.clear()
|
||||||
opcodes = self.cruncher.get_opcodes() if context is None else self.cruncher.get_grouped_opcodes(context)
|
if context is None:
|
||||||
for tag, alo, ahi, blo, bhi in opcodes:
|
for tag, alo, ahi, blo, bhi in self.cruncher.get_opcodes():
|
||||||
getattr(self, tag)(alo, ahi, blo, bhi)
|
getattr(self, tag)(alo, ahi, blo, bhi)
|
||||||
|
else:
|
||||||
|
for group in self.cruncher.get_grouped_opcodes():
|
||||||
|
for tag, alo, ahi, blo, bhi in group:
|
||||||
|
getattr(self, tag)(alo, ahi, blo, bhi)
|
||||||
|
|
||||||
for v in (self.left, self.right):
|
for v in (self.left, self.right):
|
||||||
c = v.textCursor()
|
c = v.textCursor()
|
||||||
c.movePosition(c.Start)
|
c.movePosition(c.Start)
|
||||||
v.setTextCursor(c)
|
v.setTextCursor(c)
|
||||||
|
|
||||||
def do_insert(self, cursor, lines, lo, hi):
|
def do_insert(self, cursor, highlighter, lo, hi):
|
||||||
start_block = cursor.blockNumber()
|
start_block = cursor.blockNumber()
|
||||||
if lo != hi:
|
highlighter.copy_lines(lo, hi, cursor)
|
||||||
cursor.insertText('\n'.join(lines[lo:hi]))
|
|
||||||
cursor.insertBlock()
|
|
||||||
return start_block, cursor.blockNumber()
|
return start_block, cursor.blockNumber()
|
||||||
|
|
||||||
def equal(self, alo, ahi, blo, bhi):
|
def equal(self, alo, ahi, blo, bhi):
|
||||||
@ -76,8 +137,8 @@ if __name__ == '__main__':
|
|||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
raw1 = open(sys.argv[-2], 'rb').read().decode('utf-8')
|
raw1 = open(sys.argv[-2], 'rb').read().decode('utf-8')
|
||||||
raw2 = open(sys.argv[-1], 'rb').read().decode('utf-8')
|
raw2 = open(sys.argv[-1], 'rb').read().decode('utf-8')
|
||||||
w = DiffView()
|
w = TextDiffView()
|
||||||
w(raw1, raw2)
|
w(raw1, raw2, syntax='html', context=None)
|
||||||
w.show()
|
w.show()
|
||||||
app.exec_()
|
app.exec_()
|
||||||
|
|
||||||
|
@ -27,6 +27,9 @@ from calibre.gui2.tweak_book.editor.syntax.css import CSSHighlighter
|
|||||||
PARAGRAPH_SEPARATOR = '\u2029'
|
PARAGRAPH_SEPARATOR = '\u2029'
|
||||||
entity_pat = re.compile(r'&(#{0,1}[a-zA-Z0-9]{1,8});')
|
entity_pat = re.compile(r'&(#{0,1}[a-zA-Z0-9]{1,8});')
|
||||||
|
|
||||||
|
def get_highlighter(syntax):
|
||||||
|
return {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)
|
||||||
|
|
||||||
_dff = None
|
_dff = None
|
||||||
def default_font_family():
|
def default_font_family():
|
||||||
global _dff
|
global _dff
|
||||||
@ -180,7 +183,7 @@ class TextEdit(PlainTextEdit):
|
|||||||
|
|
||||||
def load_text(self, text, syntax='html', process_template=False):
|
def load_text(self, text, syntax='html', process_template=False):
|
||||||
self.syntax = syntax
|
self.syntax = syntax
|
||||||
self.highlighter = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)(self)
|
self.highlighter = get_highlighter(syntax)(self)
|
||||||
self.highlighter.apply_theme(self.theme)
|
self.highlighter.apply_theme(self.theme)
|
||||||
self.highlighter.setDocument(self.document())
|
self.highlighter.setDocument(self.document())
|
||||||
self.setPlainText(unicodedata.normalize('NFC', text))
|
self.setPlainText(unicodedata.normalize('NFC', text))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user