##// END OF EJS Templates
Don't treat bytes objects as json-safe...
Don't treat bytes objects as json-safe closes gh-767

File last commit:

r3304:3cc304dd
r4719:6cc41ecb
Show More
pygments_highlighter.py
224 lines | 8.1 KiB | text/x-python | PythonLexer
/ IPython / frontend / qt / console / pygments_highlighter.py
epatters
Fixed imports and removed references to ETS/EPD
r2603 # System library imports.
Evan Patterson
Paved the way for PySide support....
r3304 from IPython.external.qt import QtGui
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 from pygments.formatters.html import HtmlFormatter
epatters
Initial checkin of Qt frontend code.
r2602 from pygments.lexer import RegexLexer, _TokenType, Text, Error
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 from pygments.lexers import PythonLexer
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 from pygments.styles import get_style_by_name
epatters
Initial checkin of Qt frontend code.
r2602
def get_tokens_unprocessed(self, text, stack=('root',)):
""" Split ``text`` into (tokentype, text) pairs.
Monkeypatched to store the final stack on the object itself.
"""
pos = 0
tokendefs = self._tokens
epatters
Fixed imports and removed references to ETS/EPD
r2603 if hasattr(self, '_saved_state_stack'):
statestack = list(self._saved_state_stack)
epatters
Initial checkin of Qt frontend code.
r2602 else:
statestack = list(stack)
statetokens = tokendefs[statestack[-1]]
while 1:
for rexmatch, action, new_state in statetokens:
m = rexmatch(text, pos)
if m:
if type(action) is _TokenType:
yield pos, action, m.group()
else:
for item in action(self, m):
yield item
pos = m.end()
if new_state is not None:
# state transition
if isinstance(new_state, tuple):
for state in new_state:
if state == '#pop':
statestack.pop()
elif state == '#push':
statestack.append(statestack[-1])
else:
statestack.append(state)
elif isinstance(new_state, int):
# pop
del statestack[new_state:]
elif new_state == '#push':
statestack.append(statestack[-1])
else:
assert False, "wrong state def: %r" % new_state
statetokens = tokendefs[statestack[-1]]
break
else:
try:
if text[pos] == '\n':
# at EOL, reset state to "root"
pos += 1
statestack = ['root']
statetokens = tokendefs['root']
yield pos, Text, u'\n'
continue
yield pos, Error, text[pos]
pos += 1
except IndexError:
break
epatters
Fixed imports and removed references to ETS/EPD
r2603 self._saved_state_stack = list(statestack)
epatters
Initial checkin of Qt frontend code.
r2602
# Monkeypatch!
RegexLexer.get_tokens_unprocessed = get_tokens_unprocessed
epatters
* Fleshed out IPythonWidget's style control....
r2725 class PygmentsBlockUserData(QtGui.QTextBlockUserData):
epatters
Initial checkin of Qt frontend code.
r2602 """ Storage for the user data associated with each line.
"""
syntax_stack = ('root',)
def __init__(self, **kwds):
for key, value in kwds.iteritems():
setattr(self, key, value)
QtGui.QTextBlockUserData.__init__(self)
def __repr__(self):
attrs = ['syntax_stack']
kwds = ', '.join([ '%s=%r' % (attr, getattr(self, attr))
for attr in attrs ])
epatters
* Fleshed out IPythonWidget's style control....
r2725 return 'PygmentsBlockUserData(%s)' % kwds
epatters
Initial checkin of Qt frontend code.
r2602
class PygmentsHighlighter(QtGui.QSyntaxHighlighter):
""" Syntax highlighter that uses Pygments for parsing. """
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 #---------------------------------------------------------------------------
# 'QSyntaxHighlighter' interface
#---------------------------------------------------------------------------
epatters
Initial checkin of Qt frontend code.
r2602 def __init__(self, parent, lexer=None):
super(PygmentsHighlighter, self).__init__(parent)
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 self._document = QtGui.QTextDocument()
self._formatter = HtmlFormatter(nowrap=True)
epatters
Initial checkin of Qt frontend code.
r2602 self._lexer = lexer if lexer else PythonLexer()
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 self.set_style('default')
epatters
Initial checkin of Qt frontend code.
r2602
Evan Patterson
Paved the way for PySide support....
r3304 def highlightBlock(self, string):
epatters
Initial checkin of Qt frontend code.
r2602 """ Highlight a block of text.
"""
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 prev_data = self.currentBlock().previous().userData()
epatters
Initial checkin of Qt frontend code.
r2602 if prev_data is not None:
epatters
Fixed imports and removed references to ETS/EPD
r2603 self._lexer._saved_state_stack = prev_data.syntax_stack
elif hasattr(self._lexer, '_saved_state_stack'):
del self._lexer._saved_state_stack
epatters
Initial checkin of Qt frontend code.
r2602
# Lex the text using Pygments
epatters
First cut at a generic bracket matcher for Q[Plain]TextEdits.
r2894 index = 0
Evan Patterson
Paved the way for PySide support....
r3304 for token, text in self._lexer.get_tokens(string):
epatters
First cut at a generic bracket matcher for Q[Plain]TextEdits.
r2894 length = len(text)
self.setFormat(index, length, self._get_format(token))
index += length
epatters
Initial checkin of Qt frontend code.
r2602
epatters
Fixed imports and removed references to ETS/EPD
r2603 if hasattr(self._lexer, '_saved_state_stack'):
epatters
* Fleshed out IPythonWidget's style control....
r2725 data = PygmentsBlockUserData(
syntax_stack=self._lexer._saved_state_stack)
epatters
Initial checkin of Qt frontend code.
r2602 self.currentBlock().setUserData(data)
# Clean up for the next go-round.
epatters
Fixed imports and removed references to ETS/EPD
r2603 del self._lexer._saved_state_stack
epatters
Initial checkin of Qt frontend code.
r2602
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 #---------------------------------------------------------------------------
# 'PygmentsHighlighter' interface
#---------------------------------------------------------------------------
def set_style(self, style):
""" Sets the style to the specified Pygments style.
"""
if isinstance(style, basestring):
style = get_style_by_name(style)
self._style = style
self._clear_caches()
def set_style_sheet(self, stylesheet):
""" Sets a CSS stylesheet. The classes in the stylesheet should
correspond to those generated by:
pygmentize -S <style> -f html
Note that 'set_style' and 'set_style_sheet' completely override each
other, i.e. they cannot be used in conjunction.
"""
self._document.setDefaultStyleSheet(stylesheet)
self._style = None
self._clear_caches()
#---------------------------------------------------------------------------
# Protected interface
#---------------------------------------------------------------------------
def _clear_caches(self):
""" Clear caches for brushes and formats.
epatters
Initial checkin of Qt frontend code.
r2602 """
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 self._brushes = {}
self._formats = {}
epatters
Initial checkin of Qt frontend code.
r2602
def _get_format(self, token):
""" Returns a QTextCharFormat for token or None.
"""
if token in self._formats:
return self._formats[token]
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728
if self._style is None:
result = self._get_format_from_document(token, self._document)
else:
result = self._get_format_from_style(token, self._style)
self._formats[token] = result
return result
def _get_format_from_document(self, token, document):
""" Returns a QTextCharFormat for token by
"""
code, html = self._formatter._format_lines([(token, 'dummy')]).next()
self._document.setHtml(html)
return QtGui.QTextCursor(self._document).charFormat()
def _get_format_from_style(self, token, style):
""" Returns a QTextCharFormat for token by reading a Pygments style.
"""
epatters
First cut at a generic bracket matcher for Q[Plain]TextEdits.
r2894 result = QtGui.QTextCharFormat()
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 for key, value in style.style_for_token(token).items():
epatters
Initial checkin of Qt frontend code.
r2602 if value:
if key == 'color':
result.setForeground(self._get_brush(value))
elif key == 'bgcolor':
result.setBackground(self._get_brush(value))
elif key == 'bold':
result.setFontWeight(QtGui.QFont.Bold)
elif key == 'italic':
result.setFontItalic(True)
elif key == 'underline':
result.setUnderlineStyle(
QtGui.QTextCharFormat.SingleUnderline)
elif key == 'sans':
result.setFontStyleHint(QtGui.QFont.SansSerif)
elif key == 'roman':
result.setFontStyleHint(QtGui.QFont.Times)
elif key == 'mono':
result.setFontStyleHint(QtGui.QFont.TypeWriter)
return result
def _get_brush(self, color):
""" Returns a brush for the color.
"""
result = self._brushes.get(color)
if result is None:
qcolor = self._get_color(color)
result = QtGui.QBrush(qcolor)
self._brushes[color] = result
return result
def _get_color(self, color):
epatters
* Fleshed out IPythonWidget's style control....
r2725 """ Returns a QColor built from a Pygments color string.
"""
epatters
Initial checkin of Qt frontend code.
r2602 qcolor = QtGui.QColor()
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 qcolor.setRgb(int(color[:2], base=16),
epatters
Initial checkin of Qt frontend code.
r2602 int(color[2:4], base=16),
int(color[4:6], base=16))
return qcolor