##// END OF EJS Templates
ENH: Documentation for configuring the DefaultFormatter.
ENH: Documentation for configuring the DefaultFormatter.

File last commit:

r3000:df982265
r3211:f31dfc45
Show More
ansi_code_processor.py
233 lines | 8.3 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
from PyQt4 import QtCore, QtGui
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 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
attributes.
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.)
bold_text_enabled = False
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 # Protected class variables.
_ansi_commands = 'ABCDEFGHJKSTfmnsu'
_ansi_pattern = re.compile('\x01?\x1b\[(.*?)([%s])\x02?' % _ansi_commands)
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 _special_pattern = re.compile('([\f])')
#---------------------------------------------------------------------------
# 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 = []
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
for match in self._ansi_pattern.finditer(string):
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998 raw = string[start:match.start()]
substring = self._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 = []
try:
params = []
for param in match.group(1).split(';'):
if param:
params.append(int(param))
except ValueError:
# Silently discard badly formed escape codes.
pass
else:
self.set_csi_code(match.group(2), 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:]
substring = self._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.
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:
for code in params:
self.set_sgr_code(code)
else:
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
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
def set_sgr_code(self, code):
""" Set attributes based on SGR (Select Graphic Rendition) code.
"""
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
elif code == 39:
self.foreground_color = None
elif code >= 40 and code <= 47:
self.background_color = code - 40
elif code == 49:
self.background_color = None
epatters
Added support for scroll sequences and form feeds to AnsiCodeProcessor....
r2998
#---------------------------------------------------------------------------
# Protected interface
#---------------------------------------------------------------------------
def _replace_special(self, match):
special = match.group(1)
if special == '\f':
self.actions.append(ScrollAction('scroll', 'down', 'page', 1))
return ''
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716
class QtAnsiCodeProcessor(AnsiCodeProcessor):
""" Translates ANSI escape codes into QTextCharFormats.
"""
# A map from color codes to RGB colors.
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 default_map = (# Normal, Bright/Light ANSI color code
epatters
Set more tolerable default colors for ANSI color codes.
r2784 ('black', 'grey'), # 0: black
('darkred', 'red'), # 1: red
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 ('darkgreen', 'lime'), # 2: green
('brown', 'yellow'), # 3: yellow
('darkblue', 'deepskyblue'), # 4: blue
epatters
Set more tolerable default colors for ANSI color codes.
r2784 ('darkviolet', 'magenta'), # 5: magenta
('steelblue', 'cyan'), # 6: cyan
('grey', 'white')) # 7: white
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870
def __init__(self):
super(QtAnsiCodeProcessor, self).__init__()
self.color_map = self.default_map
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
if self.foreground_color is not None:
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 color = self.color_map[self.foreground_color][self.intensity]
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 format.setForeground(QtGui.QColor(color))
# Set background color
if self.background_color is not None:
epatters
The IPythonWidget now tries to be smart about choosing colors to use for ANSI color codes.
r2870 color = self.color_map[self.background_color][self.intensity]
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 format.setBackground(QtGui.QColor(color))
# 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.
"""
if color.value() < 127:
# Colors appropriate for a terminal with a dark background.
self.color_map = self.default_map
else:
# Colors appropriate for a terminal with a light background. For
# now, only use non-bright colors...
self.color_map = [ (pair[0], pair[0]) for pair in self.default_map ]
# ...and replace white with black.
self.color_map[7] = ('black', 'black')