>>> caret_escape('^H')
8
"""
- def __init__(self, name=None, capname=None, code=None, regexp=None):
+ def __init__(self, name=None, capname=None, term=None, code=None,
+ regexp=None):
self.name = name
self.capname = capname # terminfo capability name (cf. terminfo(5))
+ if term is None:
+ term = 'ansi'
+ self.term = term
if code is None:
code = self._get_code()
self.code = code
try:
code = _curses.tigetstr(self.capname)
except _curses.error:
- term = _os.environ.get('term', 'xterm')
with open('/dev/null', 'w') as f:
# setupterm() is required for curses.tiget*()
- _curses.setupterm(term, f.fileno())
+ _curses.setupterm(self.term, f.fileno())
code = _curses.tigetstr(self.capname)
+ if code is None:
+ _LOG.warning('{} not supported on {} terminals'.format(
+ self, self.term))
# code may be None if your terminal doesn't support the capability
return code
colors = [
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']
- def __init__(self):
+ def __init__(self, term=None):
super(SelectGraphicRendition, self).__init__(
- 'select graphic rendition', 'sgr',
+ name='select graphic rendition', capname='sgr', term=term,
regexp=_re.compile(b'\x1b[[]([0-9]+)?;?([0-9]+)?m'))
def style(self, match, style=None):
return style
-CONTROL_CODES = [ # see terminfo(5)
- # Safe to ignore
- ControlCode('bell', 'bel'),
- ControlCode('meta on', 'smm'), # eight bit mode
- # Clearing the screen
- ControlCode('backspace key', 'kbs'),
- ControlCode('delete one character', 'dch1'),
- ControlCode('delete characters', 'dch'),
- ControlCode('clear screen', 'clear'),
- ControlCode('clear to beginning of line', 'el1'),
- ControlCode('clear to end of line', 'el'),
- ControlCode('clear to end of screen', 'ed'),
- # Cursor motion
- ControlCode('carriage return', 'cr'),
- ControlCode('scroll forward', 'ind'),
- ControlCode('cursor to upper left', 'home'),
- ControlCode('enter ca mode', 'smcup'), # start program using 'cup'
- ControlCode('exit ca mode', 'rmcup'), # end program using 'cup'
- ControlCode('cursor address', 'cup'),
- ControlCode('cursor up', 'cuu1'),
- ControlCode('cursor down', 'cud1'),
- ControlCode('cursor left', 'cub1'),
- ControlCode('cursor right', 'cuf1'),
- ControlCode('cursor row address', 'vpa'),
- # Cursor visibility
- ControlCode('cursor visible', 'cvvis'),
- ControlCode('cursor invisible', 'civis'),
- ControlCode('cursor normal visibility', 'cnorm'),
- # Funky stuff
- ControlCode('horizontal tab', 'ht'),
- ControlCode('insert line', 'il1'),
- ControlCode('insert line', 'il'),
- ControlCode('erase characters', 'ech'),
- ControlCode('operating system command', regexp=_re.compile(b'\x1b]')), # [1]
- ControlCode('application keypad mode', regexp=_re.compile(b'\x1b=')), # [1]
- ControlCode('numeric keypad mode', regexp=_re.compile(b'\x1b>')), # [1]
- ControlCode('set application cursor keys', regexp=_re.compile(b'\x1b[[][?]1h')), # [1]
- ControlCode('reset application cursor keys', regexp=_re.compile(b'\x1b[[][?]1l')), # [1]
- ControlCode('set scrolling region', regexp=_re.compile(b'\x1b[[]([0-9]+);([0-9]+)r')), # [1]
- ControlCode('send secondary device attributes', regexp=_re.compile(b'\x1b[[]>0c')), # [1], i.e. "what kind of device are you?" [2]
- SelectGraphicRendition(),
- ]
-# [1]: http://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html
-# [2]: http://vt100.net/docs/vt220-rm/chapter4.html#S4.17.1.2
-
-
class StyledText (object):
def __init__(self, text, style=None):
self.text = text
<span class="outside"><span style="color: cyan">@@ -8,6 +8,10 @@</span> idea</span>
"""
- def __init__(self, encoding='utf-8'):
+ def __init__(self, encoding='utf-8', term=None):
self._encoding = encoding
+ self._term = term
+ self._control_codes = self._get_control_codes()
+
+ def _get_control_codes(self):
+ # see terminfo(5)
+ return [
+ # Safe to ignore
+ ControlCode('bell', 'bel', term=self._term),
+ ControlCode('meta on', 'smm', term=self._term), # eight bit mode
+ # Clearing the screen
+ ControlCode('backspace key', 'kbs', term=self._term),
+ ControlCode('delete one character', 'dch1', term=self._term),
+ ControlCode('delete characters', 'dch', term=self._term),
+ ControlCode('clear screen', 'clear', term=self._term),
+ ControlCode('clear to beginning of line', 'el1', term=self._term),
+ ControlCode('clear to end of line', 'el', term=self._term),
+ ControlCode('clear to end of screen', 'ed', term=self._term),
+ # Cursor motion
+ ControlCode('carriage return', 'cr', term=self._term),
+ ControlCode('scroll forward', 'ind', term=self._term),
+ ControlCode('cursor to upper left', 'home', term=self._term),
+ ControlCode('enter ca mode', 'smcup', term=self._term
+ ), # start program using 'cup'
+ ControlCode('exit ca mode', 'rmcup', term=self._term
+ ), # end program using 'cup'
+ ControlCode('cursor address', 'cup', term=self._term),
+ ControlCode('cursor up', 'cuu1', term=self._term),
+ ControlCode('cursor down', 'cud1', term=self._term),
+ ControlCode('cursor left', 'cub1', term=self._term),
+ ControlCode('cursor right', 'cuf1', term=self._term),
+ ControlCode('cursor row address', 'vpa', term=self._term),
+ # Cursor visibility
+ ControlCode('cursor visible', 'cvvis', term=self._term),
+ ControlCode('cursor invisible', 'civis', term=self._term),
+ ControlCode('cursor normal visibility', 'cnorm', term=self._term),
+ # Funky stuff
+ ControlCode('horizontal tab', 'ht', term=self._term),
+ ControlCode('insert line', 'il1', term=self._term),
+ ControlCode('insert line', 'il', term=self._term),
+ ControlCode('erase characters', 'ech', term=self._term),
+ ControlCode('operating system command',
+ regexp=_re.compile(b'\x1b]'), term=self._term), # [1]
+ ControlCode('application keypad mode',
+ regexp=_re.compile(b'\x1b='), term=self._term), # [1]
+ ControlCode('numeric keypad mode',
+ regexp=_re.compile(b'\x1b>'), term=self._term), # [1]
+ ControlCode('set application cursor keys',
+ regexp=_re.compile(b'\x1b[[][?]1h'),
+ term=self._term), # [1]
+ ControlCode('reset application cursor keys',
+ regexp=_re.compile(b'\x1b[[][?]1l'),
+ term=self._term), # [1]
+ ControlCode('set scrolling region',
+ regexp=_re.compile(b'\x1b[[]([0-9]+);([0-9]+)r'),
+ term=self._term), # [1]
+ ControlCode('send secondary device attributes',
+ regexp=_re.compile(b'\x1b[[]>0c'),
+ term=self._term
+ ), # [1], i.e. "what kind of device are you?" [2]
+ SelectGraphicRendition(term=self._term),
+ ]
+ # [1]: http://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html
+ # [2]: http://vt100.net/docs/vt220-rm/chapter4.html#S4.17.1.2
def _replace_control_codes(self, text):
"""Split ``text`` into a list of chunks
``ControlCodeMatch`` instances.
"""
stack = [text]
- for cc in CONTROL_CODES:
+ for cc in self._control_codes:
if cc.regexp is None:
continue
stream = []
class ScriptParser (object):
- def __init__(self, encoding='utf-8', css=None):
- self._control_parser = ControlParser(encoding=encoding)
+ def __init__(self, encoding='utf-8', css=None, term=None):
+ self._control_parser = ControlParser(encoding=encoding, term=term)
self._encoding = encoding
if css is None:
css = self._get_css()
parser.add_argument(
'-e', '--encoding', metavar='ENCODING', default='utf-8',
help='file content encoding (defaults to utf-8)')
+ parser.add_argument(
+ '--term', metavar='TERM', default=_os.environ.get('TERM', 'ansi'),
+ help='terminal to emulate (defaults to $TERM)')
parser.add_argument(
'-p', '--poll', metavar='SECONDS', type=float,
help='poll frequency in seconds')
if args.verbose:
_LOG.setLevel(max(_logging.DEBUG, _LOG.level - 10*args.verbose))
- sp = ScriptParser(encoding=args.encoding)
+ sp = ScriptParser(encoding=args.encoding, term=args.term)
if args.serve:
server = ScriptServerApp(
- source=args.source, target=args.target, delay=args.poll or 2)
+ source=args.source, target=args.target, delay=args.poll or 2,
+ encoding=args.encoding, term=args.term)
server.run(port=args.serve)
elif args.poll:
while True: