mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Syntax highlighting in advanced profile edit view
This commit is contained in:
parent
48b6246a6a
commit
f7fe2201b8
@ -20,6 +20,7 @@ from PyQt4.QtGui import QDialog, QMessageBox
|
|||||||
from libprs500.ebooks.lrf.web.profiles import FullContentProfile, create_class
|
from libprs500.ebooks.lrf.web.profiles import FullContentProfile, create_class
|
||||||
from libprs500.gui2.dialogs.user_profiles_ui import Ui_Dialog
|
from libprs500.gui2.dialogs.user_profiles_ui import Ui_Dialog
|
||||||
from libprs500.gui2 import qstring_to_unicode, error_dialog, question_dialog
|
from libprs500.gui2 import qstring_to_unicode, error_dialog, question_dialog
|
||||||
|
from libprs500.gui2.widgets import PythonHighlighter
|
||||||
|
|
||||||
class UserProfiles(QDialog, Ui_Dialog):
|
class UserProfiles(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ class UserProfiles(QDialog, Ui_Dialog):
|
|||||||
self.toggle_mode_button.setText('Switch to Advanced mode')
|
self.toggle_mode_button.setText('Switch to Advanced mode')
|
||||||
else:
|
else:
|
||||||
self.source_code.setPlainText(src)
|
self.source_code.setPlainText(src)
|
||||||
|
self.highlighter = PythonHighlighter(self.source_code.document())
|
||||||
self.stacks.setCurrentIndex(1)
|
self.stacks.setCurrentIndex(1)
|
||||||
self.toggle_mode_button.setText('Switch to Basic mode')
|
self.toggle_mode_button.setText('Switch to Basic mode')
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ class UserProfiles(QDialog, Ui_Dialog):
|
|||||||
if not qstring_to_unicode(self.source_code.toPlainText()).strip():
|
if not qstring_to_unicode(self.source_code.toPlainText()).strip():
|
||||||
src = self.options_to_profile()[0]
|
src = self.options_to_profile()[0]
|
||||||
self.source_code.setPlainText(src.replace('BasicUserProfile', 'AdvancedUserProfile'))
|
self.source_code.setPlainText(src.replace('BasicUserProfile', 'AdvancedUserProfile'))
|
||||||
|
self.highlighter = PythonHighlighter(self.source_code.document())
|
||||||
|
|
||||||
|
|
||||||
def add_feed(self, *args):
|
def add_feed(self, *args):
|
||||||
@ -121,6 +124,7 @@ class %(classname)s(%(base_class)s):
|
|||||||
def populate_source_code(self):
|
def populate_source_code(self):
|
||||||
src = self.options_to_profile().replace('BasicUserProfile', 'AdvancedUserProfile')
|
src = self.options_to_profile().replace('BasicUserProfile', 'AdvancedUserProfile')
|
||||||
self.source_code.setPlainText(src)
|
self.source_code.setPlainText(src)
|
||||||
|
self.highlighter = PythonHighlighter(self.source_code.document())
|
||||||
|
|
||||||
def add_profile(self, clicked):
|
def add_profile(self, clicked):
|
||||||
if self.stacks.currentIndex() == 0:
|
if self.stacks.currentIndex() == 0:
|
||||||
|
@ -16,10 +16,13 @@
|
|||||||
'''
|
'''
|
||||||
Miscellanous widgets used in the GUI
|
Miscellanous widgets used in the GUI
|
||||||
'''
|
'''
|
||||||
from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, QListWidgetItem
|
from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \
|
||||||
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QSize, SIGNAL, QObject
|
QListWidgetItem, QTextCharFormat, QApplication, \
|
||||||
|
QSyntaxHighlighter, QCursor, QColor
|
||||||
|
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QSize, SIGNAL, \
|
||||||
|
QObject, QRegExp, QSettings
|
||||||
|
|
||||||
from libprs500.gui2.jobs import ConversionJob, DetailView
|
from libprs500.gui2.jobs import DetailView
|
||||||
from libprs500.gui2 import human_readable, NONE, TableView
|
from libprs500.gui2 import human_readable, NONE, TableView
|
||||||
from libprs500 import fit_image, get_font_families
|
from libprs500 import fit_image, get_font_families
|
||||||
|
|
||||||
@ -176,3 +179,244 @@ class BasicList(QListWidget):
|
|||||||
def items(self):
|
def items(self):
|
||||||
for i in range(self.count()):
|
for i in range(self.count()):
|
||||||
yield self.item(i)
|
yield self.item(i)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PythonHighlighter(QSyntaxHighlighter):
|
||||||
|
|
||||||
|
Rules = []
|
||||||
|
Formats = {}
|
||||||
|
Config = {}
|
||||||
|
|
||||||
|
KEYWORDS = ["and", "as", "assert", "break", "class", "continue", "def",
|
||||||
|
"del", "elif", "else", "except", "exec", "finally", "for", "from",
|
||||||
|
"global", "if", "import", "in", "is", "lambda", "not", "or",
|
||||||
|
"pass", "print", "raise", "return", "try", "while", "with",
|
||||||
|
"yield"]
|
||||||
|
|
||||||
|
BUILTINS = ["abs", "all", "any", "basestring", "bool", "callable", "chr",
|
||||||
|
"classmethod", "cmp", "compile", "complex", "delattr", "dict",
|
||||||
|
"dir", "divmod", "enumerate", "eval", "execfile", "exit", "file",
|
||||||
|
"filter", "float", "frozenset", "getattr", "globals", "hasattr",
|
||||||
|
"hex", "id", "int", "isinstance", "issubclass", "iter", "len",
|
||||||
|
"list", "locals", "long", "map", "max", "min", "object", "oct",
|
||||||
|
"open", "ord", "pow", "property", "range", "reduce", "repr",
|
||||||
|
"reversed", "round", "set", "setattr", "slice", "sorted",
|
||||||
|
"staticmethod", "str", "sum", "super", "tuple", "type", "unichr",
|
||||||
|
"unicode", "vars", "xrange", "zip"]
|
||||||
|
|
||||||
|
CONSTANTS = ["False", "True", "None", "NotImplemented", "Ellipsis"]
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(PythonHighlighter, self).__init__(parent)
|
||||||
|
if not self.Config:
|
||||||
|
self.loadConfig()
|
||||||
|
|
||||||
|
|
||||||
|
self.initializeFormats()
|
||||||
|
|
||||||
|
PythonHighlighter.Rules.append((QRegExp(
|
||||||
|
"|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS])),
|
||||||
|
"keyword"))
|
||||||
|
PythonHighlighter.Rules.append((QRegExp(
|
||||||
|
"|".join([r"\b%s\b" % builtin for builtin in self.BUILTINS])),
|
||||||
|
"builtin"))
|
||||||
|
PythonHighlighter.Rules.append((QRegExp(
|
||||||
|
"|".join([r"\b%s\b" % constant \
|
||||||
|
for constant in self.CONSTANTS])), "constant"))
|
||||||
|
PythonHighlighter.Rules.append((QRegExp(
|
||||||
|
r"\b[+-]?[0-9]+[lL]?\b"
|
||||||
|
r"|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b"
|
||||||
|
r"|\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"),
|
||||||
|
"number"))
|
||||||
|
PythonHighlighter.Rules.append((QRegExp(
|
||||||
|
r"\bPyQt4\b|\bQt?[A-Z][a-z]\w+\b"), "pyqt"))
|
||||||
|
PythonHighlighter.Rules.append((QRegExp(r"\b@\w+\b"), "decorator"))
|
||||||
|
stringRe = QRegExp(r"""(?:'[^']*'|"[^"]*")""")
|
||||||
|
stringRe.setMinimal(True)
|
||||||
|
PythonHighlighter.Rules.append((stringRe, "string"))
|
||||||
|
self.stringRe = QRegExp(r"""(:?"["]".*"["]"|'''.*''')""")
|
||||||
|
self.stringRe.setMinimal(True)
|
||||||
|
PythonHighlighter.Rules.append((self.stringRe, "string"))
|
||||||
|
self.tripleSingleRe = QRegExp(r"""'''(?!")""")
|
||||||
|
self.tripleDoubleRe = QRegExp(r'''"""(?!')''')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def loadConfig(cls):
|
||||||
|
Config = cls.Config
|
||||||
|
def setDefaultString(name, default):
|
||||||
|
value = settings.value(name).toString()
|
||||||
|
if value.isEmpty():
|
||||||
|
value = default
|
||||||
|
Config[name] = value
|
||||||
|
|
||||||
|
settings = QSettings()
|
||||||
|
for name in ("window", "shell"):
|
||||||
|
Config["%swidth" % name] = settings.value("%swidth" % name,
|
||||||
|
QVariant(QApplication.desktop() \
|
||||||
|
.availableGeometry().width() / 2)).toInt()[0]
|
||||||
|
Config["%sheight" % name] = settings.value("%sheight" % name,
|
||||||
|
QVariant(QApplication.desktop() \
|
||||||
|
.availableGeometry().height() / 2)).toInt()[0]
|
||||||
|
Config["%sy" % name] = settings.value("%sy" % name,
|
||||||
|
QVariant(0)).toInt()[0]
|
||||||
|
Config["toolbars"] = settings.value("toolbars").toByteArray()
|
||||||
|
Config["splitter"] = settings.value("splitter").toByteArray()
|
||||||
|
Config["shellx"] = settings.value("shellx", QVariant(0)).toInt()[0]
|
||||||
|
Config["windowx"] = settings.value("windowx", QVariant(QApplication \
|
||||||
|
.desktop().availableGeometry().width() / 2)).toInt()[0]
|
||||||
|
Config["remembergeometry"] = settings.value("remembergeometry",
|
||||||
|
QVariant(True)).toBool()
|
||||||
|
Config["startwithshell"] = settings.value("startwithshell",
|
||||||
|
QVariant(True)).toBool()
|
||||||
|
Config["showwindowinfo"] = settings.value("showwindowinfo",
|
||||||
|
QVariant(True)).toBool()
|
||||||
|
setDefaultString("shellstartup", """\
|
||||||
|
from __future__ import division
|
||||||
|
import codecs
|
||||||
|
import sys
|
||||||
|
sys.stdin = codecs.getreader("UTF8")(sys.stdin)
|
||||||
|
sys.stdout = codecs.getwriter("UTF8")(sys.stdout)""")
|
||||||
|
setDefaultString("newfile", """\
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
|
import sys
|
||||||
|
""")
|
||||||
|
Config["backupsuffix"] = settings.value("backupsuffix",
|
||||||
|
QVariant(".bak")).toString()
|
||||||
|
setDefaultString("beforeinput", "#>>>")
|
||||||
|
setDefaultString("beforeoutput", "#---")
|
||||||
|
Config["cwd"] = settings.value("cwd", QVariant(".")).toString()
|
||||||
|
Config["tooltipsize"] = settings.value("tooltipsize",
|
||||||
|
QVariant(150)).toInt()[0]
|
||||||
|
Config["maxlinestoscan"] = settings.value("maxlinestoscan",
|
||||||
|
QVariant(5000)).toInt()[0]
|
||||||
|
Config["pythondocpath"] = settings.value("pythondocpath",
|
||||||
|
QVariant("http://docs.python.org")).toString()
|
||||||
|
Config["autohidefinddialog"] = settings.value("autohidefinddialog",
|
||||||
|
QVariant(True)).toBool()
|
||||||
|
Config["findcasesensitive"] = settings.value("findcasesensitive",
|
||||||
|
QVariant(False)).toBool()
|
||||||
|
Config["findwholewords"] = settings.value("findwholewords",
|
||||||
|
QVariant(False)).toBool()
|
||||||
|
Config["tabwidth"] = settings.value("tabwidth",
|
||||||
|
QVariant(4)).toInt()[0]
|
||||||
|
Config["fontfamily"] = settings.value("fontfamily",
|
||||||
|
QVariant("Bitstream Vera Sans Mono")).toString()
|
||||||
|
Config["fontsize"] = settings.value("fontsize",
|
||||||
|
QVariant(10)).toInt()[0]
|
||||||
|
for name, color, bold, italic in (
|
||||||
|
("normal", "#000000", False, False),
|
||||||
|
("keyword", "#000080", True, False),
|
||||||
|
("builtin", "#0000A0", False, False),
|
||||||
|
("constant", "#0000C0", False, False),
|
||||||
|
("decorator", "#0000E0", False, False),
|
||||||
|
("comment", "#007F00", False, True),
|
||||||
|
("string", "#808000", False, False),
|
||||||
|
("number", "#924900", False, False),
|
||||||
|
("error", "#FF0000", False, False),
|
||||||
|
("pyqt", "#50621A", False, False)):
|
||||||
|
Config["%sfontcolor" % name] = settings.value(
|
||||||
|
"%sfontcolor" % name, QVariant(color)).toString()
|
||||||
|
Config["%sfontbold" % name] = settings.value(
|
||||||
|
"%sfontbold" % name, QVariant(bold)).toBool()
|
||||||
|
Config["%sfontitalic" % name] = settings.value(
|
||||||
|
"%sfontitalic" % name, QVariant(italic)).toBool()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initializeFormats(cls):
|
||||||
|
Config = cls.Config
|
||||||
|
baseFormat = QTextCharFormat()
|
||||||
|
baseFormat.setFontFamily(Config["fontfamily"])
|
||||||
|
baseFormat.setFontPointSize(Config["fontsize"])
|
||||||
|
for name in ("normal", "keyword", "builtin", "constant",
|
||||||
|
"decorator", "comment", "string", "number", "error",
|
||||||
|
"pyqt"):
|
||||||
|
format = QTextCharFormat(baseFormat)
|
||||||
|
format.setForeground(QColor(Config["%sfontcolor" % name]))
|
||||||
|
if Config["%sfontbold" % name]:
|
||||||
|
format.setFontWeight(QFont.Bold)
|
||||||
|
format.setFontItalic(Config["%sfontitalic" % name])
|
||||||
|
PythonHighlighter.Formats[name] = format
|
||||||
|
|
||||||
|
|
||||||
|
def highlightBlock(self, text):
|
||||||
|
NORMAL, TRIPLESINGLE, TRIPLEDOUBLE, ERROR = range(4)
|
||||||
|
|
||||||
|
textLength = text.length()
|
||||||
|
prevState = self.previousBlockState()
|
||||||
|
|
||||||
|
self.setFormat(0, textLength,
|
||||||
|
PythonHighlighter.Formats["normal"])
|
||||||
|
|
||||||
|
if text.startsWith("Traceback") or text.startsWith("Error: "):
|
||||||
|
self.setCurrentBlockState(ERROR)
|
||||||
|
self.setFormat(0, textLength,
|
||||||
|
PythonHighlighter.Formats["error"])
|
||||||
|
return
|
||||||
|
if prevState == ERROR and \
|
||||||
|
not (text.startsWith('>>>') or text.startsWith("#")):
|
||||||
|
self.setCurrentBlockState(ERROR)
|
||||||
|
self.setFormat(0, textLength,
|
||||||
|
PythonHighlighter.Formats["error"])
|
||||||
|
return
|
||||||
|
|
||||||
|
for regex, format in PythonHighlighter.Rules:
|
||||||
|
i = text.indexOf(regex)
|
||||||
|
while i >= 0:
|
||||||
|
length = regex.matchedLength()
|
||||||
|
self.setFormat(i, length,
|
||||||
|
PythonHighlighter.Formats[format])
|
||||||
|
i = text.indexOf(regex, i + length)
|
||||||
|
|
||||||
|
# Slow but good quality highlighting for comments. For more
|
||||||
|
# speed, comment this out and add the following to __init__:
|
||||||
|
# PythonHighlighter.Rules.append((QRegExp(r"#.*"), "comment"))
|
||||||
|
if text.isEmpty():
|
||||||
|
pass
|
||||||
|
elif text[0] == "#":
|
||||||
|
self.setFormat(0, text.length(),
|
||||||
|
PythonHighlighter.Formats["comment"])
|
||||||
|
else:
|
||||||
|
stack = []
|
||||||
|
for i, c in enumerate(text):
|
||||||
|
if c in ('"', "'"):
|
||||||
|
if stack and stack[-1] == c:
|
||||||
|
stack.pop()
|
||||||
|
else:
|
||||||
|
stack.append(c)
|
||||||
|
elif c == "#" and len(stack) == 0:
|
||||||
|
self.setFormat(i, text.length(),
|
||||||
|
PythonHighlighter.Formats["comment"])
|
||||||
|
break
|
||||||
|
|
||||||
|
self.setCurrentBlockState(NORMAL)
|
||||||
|
|
||||||
|
if text.indexOf(self.stringRe) != -1:
|
||||||
|
return
|
||||||
|
# This is fooled by triple quotes inside single quoted strings
|
||||||
|
for i, state in ((text.indexOf(self.tripleSingleRe),
|
||||||
|
TRIPLESINGLE),
|
||||||
|
(text.indexOf(self.tripleDoubleRe),
|
||||||
|
TRIPLEDOUBLE)):
|
||||||
|
if self.previousBlockState() == state:
|
||||||
|
if i == -1:
|
||||||
|
i = text.length()
|
||||||
|
self.setCurrentBlockState(state)
|
||||||
|
self.setFormat(0, i + 3,
|
||||||
|
PythonHighlighter.Formats["string"])
|
||||||
|
elif i > -1:
|
||||||
|
self.setCurrentBlockState(state)
|
||||||
|
self.setFormat(i, text.length(),
|
||||||
|
PythonHighlighter.Formats["string"])
|
||||||
|
|
||||||
|
|
||||||
|
def rehighlight(self):
|
||||||
|
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
|
QSyntaxHighlighter.rehighlight(self)
|
||||||
|
QApplication.restoreOverrideCursor()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user