##// END OF EJS Templates
Use template inheritance.
Use template inheritance.

File last commit:

r4872:34c10438
r5324:233e9c0f
Show More
ansi_code_processor.py
334 lines | 11.7 KiB | text/x-python | PythonLexer
/ IPython / frontend / qt / console / ansi_code_processor.py
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 """ Utilities for processing ANSI escape codes and special ASCII characters.
"""
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 # Standard library imports
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 from collections import namedtuple
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 import re
# System library imports
Evan Patterson
Paved the way for PySide support....
r3304 from IPython.external.qt import QtCore, QtGui
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 #-----------------------------------------------------------------------------
# Constants and datatypes
#-----------------------------------------------------------------------------
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 # An action for erase requests (ED and EL commands).
EraseAction = namedtuple('EraseAction', ['action', 'area', 'erase_to'])
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 # An action for cursor move requests (CUU, CUD, CUF, CUB, CNL, CPL, CHA, CUP,
# and HVP commands).
# FIXME: Not implemented in AnsiCodeProcessor.
MoveAction = namedtuple('MoveAction', ['action', 'dir', 'unit', 'count'])
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 # An action for scroll requests (SU and ST) and form feeds.
ScrollAction = namedtuple('ScrollAction', ['action', 'dir', 'unit', 'count'])
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # Regular expressions.
CSI_COMMANDS = 'ABCDEFGHJKSTfmnsu'
CSI_SUBPATTERN = '\[(.*?)([%s])' % CSI_COMMANDS
OSC_SUBPATTERN = '\](.*?)[\x07\x1b]'
ANSI_PATTERN = re.compile('\x01?\x1b(%s|%s)\x02?' % \
(CSI_SUBPATTERN, OSC_SUBPATTERN))
SPECIAL_PATTERN = re.compile('([\f])')
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 #-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 class AnsiCodeProcessor(object):
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 """ Translates special ASCII characters and ANSI escape codes into readable
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 attributes. It also supports a few non-standard, xterm-specific codes.
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 """
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 # Whether to increase intensity or set boldness for SGR code 1.
# (Different terminals handle this in different ways.)
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 bold_text_enabled = False
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # We provide an empty default color map because subclasses will likely want
# to use a custom color format.
default_color_map = {}
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998
#---------------------------------------------------------------------------
# AnsiCodeProcessor interface
#---------------------------------------------------------------------------
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
def __init__(self):
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 self.actions = []
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 self.color_map = self.default_color_map.copy()
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 self.reset_sgr()
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 def reset_sgr(self):
""" Reset graphics attributs to their default values.
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 """
self.intensity = 0
self.italic = False
self.bold = False
self.underline = False
self.foreground_color = None
self.background_color = None
def split_string(self, string):
""" Yields substrings for which the same escape code applies.
"""
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 self.actions = []
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 start = 0
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 for match in ANSI_PATTERN.finditer(string):
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 raw = string[start:match.start()]
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 substring = SPECIAL_PATTERN.sub(self._replace_special, raw)
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 if substring or self.actions:
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 yield substring
start = match.end()
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 self.actions = []
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 groups = filter(lambda x: x is not None, match.groups())
params = [ param for param in groups[1].split(';') if param ]
if groups[0].startswith('['):
# Case 1: CSI code.
try:
params = map(int, params)
except ValueError:
# Silently discard badly formed codes.
pass
else:
self.set_csi_code(groups[2], params)
elif groups[0].startswith(']'):
# Case 2: OSC code.
self.set_osc_code(params)
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 raw = string[start:]
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 substring = SPECIAL_PATTERN.sub(self._replace_special, raw)
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 if substring or self.actions:
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 yield substring
def set_csi_code(self, command, params=[]):
""" Set attributes based on CSI (Control Sequence Introducer) code.
Parameters
----------
command : str
The code identifier, i.e. the final character in the sequence.
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 params : sequence of integers, optional
The parameter codes for the command.
"""
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 if command == 'm': # SGR - Select Graphic Rendition
epatters
Fixed ANSI compliance issue in AnsiCodeProcessor....
r3000 if params:
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 self.set_sgr_code(params)
epatters
Fixed ANSI compliance issue in AnsiCodeProcessor....
r3000 else:
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 self.set_sgr_code([0])
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783
elif (command == 'J' or # ED - Erase Data
command == 'K'): # EL - Erase in Line
code = params[0] if params else 0
if 0 <= code <= 2:
area = 'screen' if command == 'J' else 'line'
if code == 0:
erase_to = 'end'
elif code == 1:
erase_to = 'start'
elif code == 2:
erase_to = 'all'
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 self.actions.append(EraseAction('erase', area, erase_to))
elif (command == 'S' or # SU - Scroll Up
command == 'T'): # SD - Scroll Down
dir = 'up' if command == 'S' else 'down'
count = params[0] if params else 1
self.actions.append(ScrollAction('scroll', dir, 'line', count))
epatters
Added 256-color support to Qt console escape sequence processing.
r3367
def set_osc_code(self, params):
""" Set attributes based on OSC (Operating System Command) parameters.
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 Parameters
----------
params : sequence of str
The parameters for the command.
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 """
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 try:
command = int(params.pop(0))
except (IndexError, ValueError):
return
if command == 4:
# xterm-specific: set color number to color spec.
try:
color = int(params.pop(0))
spec = params.pop(0)
self.color_map[color] = self._parse_xterm_color_spec(spec)
except (IndexError, ValueError):
pass
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 def set_sgr_code(self, params):
""" Set attributes based on SGR (Select Graphic Rendition) codes.
Parameters
----------
params : sequence of ints
A list of SGR codes for one or more SGR commands. Usually this
sequence will have one element per command, although certain
xterm-specific commands requires multiple elements.
"""
# Always consume the first parameter.
if not params:
return
code = params.pop(0)
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 if code == 0:
epatters
Added support for ANSI erase codes. Clearing the console via ANSI escape sequences is now supported.
r2783 self.reset_sgr()
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 elif code == 1:
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 if self.bold_text_enabled:
self.bold = True
else:
self.intensity = 1
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 elif code == 2:
self.intensity = 0
elif code == 3:
self.italic = True
elif code == 4:
self.underline = True
elif code == 22:
self.intensity = 0
self.bold = False
elif code == 23:
self.italic = False
elif code == 24:
self.underline = False
elif code >= 30 and code <= 37:
self.foreground_color = code - 30
Bernardo B. Marques
remove all trailling spaces
r4872 elif code == 38 and params and params.pop(0) == 5:
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # xterm-specific: 256 color support.
if params:
self.foreground_color = params.pop(0)
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 elif code == 39:
self.foreground_color = None
elif code >= 40 and code <= 47:
self.background_color = code - 40
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 elif code == 48 and params and params.pop(0) == 5:
# xterm-specific: 256 color support.
if params:
self.background_color = params.pop(0)
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 elif code == 49:
self.background_color = None
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # Recurse with unconsumed parameters.
self.set_sgr_code(params)
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 #---------------------------------------------------------------------------
# Protected interface
#---------------------------------------------------------------------------
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 def _parse_xterm_color_spec(self, spec):
if spec.startswith('rgb:'):
return tuple(map(lambda x: int(x, 16), spec[4:].split('/')))
elif spec.startswith('rgbi:'):
Bernardo B. Marques
remove all trailling spaces
r4872 return tuple(map(lambda x: int(float(x) * 255),
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 spec[5:].split('/')))
elif spec == '?':
raise ValueError('Unsupported xterm color spec')
return spec
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 def _replace_special(self, match):
special = match.group(1)
if special == '\f':
self.actions.append(ScrollAction('scroll', 'down', 'page', 1))
return ''
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
class QtAnsiCodeProcessor(AnsiCodeProcessor):
""" Translates ANSI escape codes into QTextCharFormats.
"""
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # A map from ANSI color codes to SVG color names or RGB(A) tuples.
darkbg_color_map = {
0 : 'black', # black
1 : 'darkred', # red
2 : 'darkgreen', # green
3 : 'brown', # yellow
4 : 'darkblue', # blue
5 : 'darkviolet', # magenta
6 : 'steelblue', # cyan
7 : 'grey', # white
8 : 'grey', # black (bright)
9 : 'red', # red (bright)
10 : 'lime', # green (bright)
11 : 'yellow', # yellow (bright)
12 : 'deepskyblue', # blue (bright)
13 : 'magenta', # magenta (bright)
14 : 'cyan', # cyan (bright)
15 : 'white' } # white (bright)
# Set the default color map for super class.
default_color_map = darkbg_color_map.copy()
def get_color(self, color, intensity=0):
""" Returns a QColor for a given color code, or None if one cannot be
constructed.
"""
if color is None:
return None
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # Adjust for intensity, if possible.
if color < 8 and intensity > 0:
color += 8
constructor = self.color_map.get(color, None)
if isinstance(constructor, basestring):
# If this is an X11 color name, we just hope there is a close SVG
# color name. We could use QColor's static method
# 'setAllowX11ColorNames()', but this is global and only available
# on X11. It seems cleaner to aim for uniformity of behavior.
return QtGui.QColor(constructor)
elif isinstance(constructor, (tuple, list)):
return QtGui.QColor(*constructor)
return None
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 def get_format(self):
""" Returns a QTextCharFormat that encodes the current style attributes.
"""
format = QtGui.QTextCharFormat()
# Set foreground color
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 qcolor = self.get_color(self.foreground_color, self.intensity)
if qcolor is not None:
format.setForeground(qcolor)
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
# Set background color
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 qcolor = self.get_color(self.background_color, self.intensity)
if qcolor is not None:
format.setBackground(qcolor)
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
# Set font weight/style options
if self.bold:
format.setFontWeight(QtGui.QFont.Bold)
else:
format.setFontWeight(QtGui.QFont.Normal)
format.setFontItalic(self.italic)
format.setFontUnderline(self.underline)
return format
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870
def set_background_color(self, color):
""" Given a background color (a QColor), attempt to set a color map
that will be aesthetically pleasing.
"""
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 # Set a new default color map.
self.default_color_map = self.darkbg_color_map.copy()
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 if color.value() >= 127:
Bernardo B. Marques
remove all trailling spaces
r4872 # Colors appropriate for a terminal with a light background. For
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 # now, only use non-bright colors...
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 for i in xrange(8):
self.default_color_map[i + 8] = self.default_color_map[i]
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870
# ...and replace white with black.
epatters
Added 256-color support to Qt console escape sequence processing.
r3367 self.default_color_map[7] = self.default_color_map[15] = 'black'
# Update the current color map with the new defaults.
self.color_map.update(self.default_color_map)