Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pilas/console/highlighter.py
diff options
context:
space:
mode:
Diffstat (limited to 'pilas/console/highlighter.py')
-rw-r--r--pilas/console/highlighter.py299
1 files changed, 299 insertions, 0 deletions
diff --git a/pilas/console/highlighter.py b/pilas/console/highlighter.py
new file mode 100644
index 0000000..a17ab20
--- /dev/null
+++ b/pilas/console/highlighter.py
@@ -0,0 +1,299 @@
+#-*-coding:utf-8-*-
+from __future__ import absolute_import
+# based on Python Syntax highlighting from:
+# http://diotavelli.net/PyQtWiki/Python%20syntax%20highlighting
+
+import os
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+from PyQt4.QtGui import QColor
+from PyQt4.QtGui import QTextCharFormat
+from PyQt4.QtGui import QFont
+from PyQt4.QtGui import QSyntaxHighlighter
+from PyQt4.QtCore import QRegExp
+
+
+COLOR_SCHEME = {
+ "keyword": "darkMagenta",
+ "operator": "darkRed",
+ "brace": "#858585",
+ "definition": "black",
+ "string": "green",
+ "string2": "darkGreen",
+ "comment": "gray",
+ "properObject": "darkBlue",
+ "numbers": "brown",
+ "spaces": "#BFBFBF",
+ "extras": "orange",
+ "editor-background": "white",
+ "editor-selection-color": "white",
+ "editor-selection-background": "#437DCD",
+ "editor-text": "black",
+ "current-line": "darkCyan",
+ "selected-word": "yellow",
+ "brace-background": "#5BC85B",
+ "brace-foreground": "red"}
+FONT_FAMILY = 'Monospace'
+FONT_SIZE = 11
+SYNTAX = {}
+
+
+def load_syntax():
+ syntax_file = os.path.join(os.path.dirname(__file__), 'lang',
+ 'python.json')
+ structure = None
+ read = open(syntax_file, 'r')
+ structure = json.load(read)
+ read.close()
+ SYNTAX['python'] = structure
+
+
+def format(color, style=''):
+ """Return a QTextCharFormat with the given attributes."""
+ _color = QColor()
+ _color.setNamedColor(color)
+
+ _format = QTextCharFormat()
+ _format.setFontFamily(FONT_FAMILY)
+ _format.setForeground(_color)
+ if 'bold' in style:
+ _format.setFontWeight(QFont.Bold)
+ if 'italic' in style:
+ _format.setFontItalic(True)
+
+ return _format
+
+
+# Syntax styles that can be shared by all languages
+STYLES = {
+ 'keyword': format(COLOR_SCHEME['keyword'], 'bold'),
+ 'operator': format(COLOR_SCHEME['operator']),
+ 'brace': format(COLOR_SCHEME['brace']),
+ 'definition': format(COLOR_SCHEME['definition'], 'bold'),
+ 'string': format(COLOR_SCHEME['string']),
+ 'string2': format(COLOR_SCHEME['string2']),
+ 'comment': format(COLOR_SCHEME['comment'], 'italic'),
+ 'properObject': format(COLOR_SCHEME['properObject'], 'italic'),
+ 'numbers': format(COLOR_SCHEME['numbers']),
+ 'spaces': format(COLOR_SCHEME['spaces']),
+ 'extras': format(COLOR_SCHEME['extras'])}
+
+
+def restyle(scheme):
+ STYLES['keyword'] = format(scheme.get('keyword',
+ COLOR_SCHEME['keyword']), 'bold')
+ STYLES['operator'] = format(scheme.get('operator',
+ COLOR_SCHEME['operator']))
+ STYLES['brace'] = format(scheme.get('brace',
+ COLOR_SCHEME['brace']))
+ STYLES['definition'] = format(scheme.get('definition',
+ COLOR_SCHEME['definition']), 'bold')
+ STYLES['string'] = format(scheme.get('string',
+ COLOR_SCHEME['string']))
+ STYLES['string2'] = format(scheme.get('string2',
+ COLOR_SCHEME['string2']))
+ STYLES['comment'] = format(scheme.get('comment',
+ COLOR_SCHEME['comment']), 'italic')
+ STYLES['properObject'] = format(scheme.get('properObject',
+ COLOR_SCHEME['properObject']), 'italic')
+ STYLES['numbers'] = format(scheme.get('numbers',
+ COLOR_SCHEME['numbers']))
+ STYLES['spaces'] = format(scheme.get('spaces',
+ COLOR_SCHEME['spaces']))
+ STYLES['extras'] = format(scheme.get('extras',
+ COLOR_SCHEME['extras']))
+
+
+class Highlighter (QSyntaxHighlighter):
+
+ # braces
+ braces = ['\\(', '\\)', '\\{', '\\}', '\\[', '\\]']
+
+ def __init__(self, document, lang, scheme=None):
+ QSyntaxHighlighter.__init__(self, document)
+ self.apply_highlight(lang, scheme)
+
+ def apply_highlight(self, lang, scheme=None):
+ load_syntax()
+ langSyntax = SYNTAX.get(lang, {})
+ if scheme:
+ restyle(scheme)
+
+ keywords = langSyntax.get('keywords', [])
+ operators = langSyntax.get('operators', [])
+ extras = langSyntax.get('extras', [])
+
+ rules = []
+
+ # Keyword, operator, brace and extras rules
+ rules += [(r'\b%s\b' % w, 0, STYLES['keyword'])
+ for w in keywords]
+ rules += [(r'%s' % o, 0, STYLES['operator'])
+ for o in operators]
+ rules += [(r'%s' % b, 0, STYLES['brace'])
+ for b in Highlighter.braces]
+ rules += [(r'\b%s\b' % e, 0, STYLES['extras'])
+ for e in extras]
+
+ # All other rules
+ proper = langSyntax.get('properObject', None)
+ if proper is not None:
+ proper = '\\b' + str(proper[0]) + '\\b'
+ rules += [(proper, 0, STYLES['properObject'])]
+
+ rules.append((r'__\w+__', 0, STYLES['properObject']))
+
+ definition = langSyntax.get('definition', [])
+ for de in definition:
+ expr = '\\b' + de + '\\b\\s*(\\w+)'
+ rules.append((expr, 1, STYLES['definition']))
+
+ # Numeric literals
+ rules += [
+ (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
+ (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
+ (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
+ STYLES['numbers']),
+ ]
+
+ regex = langSyntax.get('regex', [])
+ for reg in regex:
+ expr = reg[0]
+ color = COLOR_SCHEME['extras']
+ style = ''
+ if len(reg) > 1:
+ color = COLOR_SCHEME[reg[1]]
+ if len(reg) > 2:
+ style = reg[2]
+ rules.append((expr, 0, format(color, style)))
+
+ comments = langSyntax.get('comment', [])
+ for co in comments:
+ expr = co + '[^\\n]*'
+ rules.append((expr, 0, STYLES['comment']))
+
+ stringChar = langSyntax.get('string', [])
+ for sc in stringChar:
+ expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' \
+ else r"'[^'\\]*(\\.[^'\\]*)*'"
+ rules.append((expr, 0, STYLES['string']))
+
+ # Multi-line strings (expression, flag, style)
+ # FIXME: The triple-quotes in these two lines will mess up the
+ # syntax highlighting from this point onward
+ self.tri_single = (QRegExp("'''"), 1, STYLES['string2']) # '''
+ self.tri_double = (QRegExp('"""'), 2, STYLES['string2']) # """
+
+ multi = langSyntax.get('multiline_comment', [])
+ if multi:
+ self.multi_start = (QRegExp(multi['open']), STYLES['comment'])
+ self.multi_end = (QRegExp(multi['close']), STYLES['comment'])
+ else:
+ self.multi_start = None
+
+ # Build a QRegExp for each pattern
+ self.rules = [(QRegExp(pat), index, fmt)
+ for (pat, index, fmt) in rules]
+ #Apply Highlight to the document... (when colors change)
+ self.rehighlight()
+
+ def highlightBlock(self, text):
+ """Apply syntax highlighting to the given block of text."""
+ for expression, nth, format in self.rules:
+ index = expression.indexIn(text, 0)
+
+ while index >= 0:
+ # We actually want the index of the nth match
+ index = expression.pos(nth)
+ length = expression.cap(nth).length()
+ self.setFormat(index, length, format)
+ index = expression.indexIn(text, index + length)
+
+ self.setCurrentBlockState(0)
+ if not self.multi_start:
+ # Do multi-line strings
+ in_multiline = self.match_multiline(text, *self.tri_single)
+ if not in_multiline:
+ in_multiline = self.match_multiline(text, *self.tri_double)
+ else:
+ # Do multi-line comment
+ self.comment_multiline(text, self.multi_end[0], *self.multi_start)
+
+ #Spaces
+ expression = QRegExp('\s+')
+ index = expression.indexIn(text, 0)
+ while index >= 0:
+ index = expression.pos(0)
+ length = expression.cap(0).length()
+ self.setFormat(index, length, STYLES['spaces'])
+ index = expression.indexIn(text, index + length)
+
+ def match_multiline(self, text, delimiter, in_state, style):
+ """Do highlighting of multi-line strings. ``delimiter`` should be a
+ ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
+ ``in_state`` should be a unique integer to represent the corresponding
+ state changes when inside those strings. Returns True if we're still
+ inside a multi-line string when this function is finished.
+ """
+ # If inside triple-single quotes, start at 0
+ if self.previousBlockState() == in_state:
+ start = 0
+ add = 0
+ # Otherwise, look for the delimiter on this line
+ else:
+ start = delimiter.indexIn(text)
+ # Move past this match
+ add = delimiter.matchedLength()
+
+ # As long as there's a delimiter match on this line...
+ while start >= 0:
+ # Look for the ending delimiter
+ end = delimiter.indexIn(text, start + add)
+ # Ending delimiter on this line?
+ if end >= add:
+ length = end - start + add + delimiter.matchedLength()
+ self.setCurrentBlockState(0)
+ # No; multi-line string
+ else:
+ self.setCurrentBlockState(in_state)
+ length = text.length() - start + add
+ # Apply formatting
+ self.setFormat(start, length, style)
+ # Look for the next match
+ start = delimiter.indexIn(text, start + length)
+
+ # Return True if still inside a multi-line string, False otherwise
+ if self.currentBlockState() == in_state:
+ return True
+ else:
+ return False
+
+ def comment_multiline(self, text, delimiter_end, delimiter_start, style):
+ startIndex = 0
+ if self.previousBlockState() != 1:
+ startIndex = delimiter_start.indexIn(text)
+ while startIndex >= 0:
+ endIndex = delimiter_end.indexIn(text, startIndex)
+ commentLength = 0
+ if endIndex == -1:
+ self.setCurrentBlockState(1)
+ commentLength = text.length() - startIndex
+ else:
+ commentLength = endIndex - startIndex + \
+ delimiter_end.matchedLength()
+
+ self.setFormat(startIndex, commentLength, style)
+ startIndex = delimiter_start.indexIn(text,
+ startIndex + commentLength)
+
+
+class EmptyHighlighter (QSyntaxHighlighter):
+
+ def __init__(self, document):
+ QSyntaxHighlighter.__init__(self, document)
+
+ def highlightBlock(self, text):
+ pass