mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 08:26:56 +00:00
Import pyte
This commit is contained in:
parent
de072b3b42
commit
d993a5732f
13 changed files with 2550 additions and 1 deletions
14
pyte/AUTHORS
Normal file
14
pyte/AUTHORS
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Authors
|
||||
=======
|
||||
|
||||
- George Shuklin
|
||||
- Sergei Lebedev
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
- Alexey Shamrin
|
||||
- Steve Cohen
|
||||
- Jonathan Slenders
|
||||
- David O'Shea
|
||||
- Andreas Stührk
|
||||
165
pyte/LICENSE
Normal file
165
pyte/LICENSE
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
53
pyte/__init__.py
Normal file
53
pyte/__init__.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte
|
||||
~~~~
|
||||
|
||||
`pyte` implements a mix of VT100, VT220 and VT520 specification,
|
||||
and aims to support most of the `TERM=linux` functionality.
|
||||
|
||||
Two classes: :class:`~pyte.streams.Stream`, which parses the
|
||||
command stream and dispatches events for commands, and
|
||||
:class:`~pyte.screens.Screen` which, when used with a stream
|
||||
maintains a buffer of strings representing the screen of a
|
||||
terminal.
|
||||
|
||||
.. warning:: From ``xterm/main.c`` "If you think you know what all
|
||||
of this code is doing, you are probably very mistaken.
|
||||
There be serious and nasty dragons here" -- nothing
|
||||
has changed.
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
__all__ = ("Screen", "DiffScreen", "HistoryScreen",
|
||||
"Stream", "ByteStream", "DebugStream")
|
||||
|
||||
import io
|
||||
|
||||
from .screens import Screen, DiffScreen, HistoryScreen
|
||||
from .streams import Stream, ByteStream, DebugStream
|
||||
|
||||
|
||||
if __debug__:
|
||||
from .compat import str
|
||||
|
||||
def dis(chars):
|
||||
"""A :func:`dis.dis` for terminals.
|
||||
|
||||
>>> dis(b"\x07") # doctest: +NORMALIZE_WHITESPACE
|
||||
BELL
|
||||
>>> dis(b"\x1b[20m") # doctest: +NORMALIZE_WHITESPACE
|
||||
SELECT_GRAPHIC_RENDITION 20
|
||||
"""
|
||||
if isinstance(chars, str):
|
||||
chars = chars.encode("utf-8")
|
||||
|
||||
with io.StringIO() as buf:
|
||||
DebugStream(to=buf).feed(chars)
|
||||
print(buf.getvalue())
|
||||
33
pyte/__main__.py
Normal file
33
pyte/__main__.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte
|
||||
~~~~
|
||||
|
||||
Command-line tool for "disassembling" escape and CSI sequences::
|
||||
|
||||
$ echo -e "\e[Jfoo" | python -m pyte
|
||||
ERASE_IN_DISPLAY 0
|
||||
DRAW f
|
||||
DRAW o
|
||||
DRAW o
|
||||
LINEFEED
|
||||
|
||||
$ python -m pyte foo
|
||||
DRAW f
|
||||
DRAW o
|
||||
DRAW o
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import pyte
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
pyte.dis(sys.stdin.read())
|
||||
else:
|
||||
pyte.dis("".join(sys.argv[1:]))
|
||||
143
pyte/charsets.py
Normal file
143
pyte/charsets.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.charsets
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
This module defines ``G0`` and ``G1`` charset mappings the same way
|
||||
they are defined for linux terminal, see
|
||||
``linux/drivers/tty/consolemap.c`` @ http://git.kernel.org
|
||||
|
||||
.. note:: ``VT100_MAP`` and ``IBMPC_MAP`` were taken unchanged
|
||||
from linux kernel source and therefore are licensed
|
||||
under **GPL**.
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .compat import chr, map
|
||||
|
||||
|
||||
#: Latin1.
|
||||
LAT1_MAP = "".join(map(chr, range(256)))
|
||||
|
||||
#: VT100 graphic character set.
|
||||
VT100_MAP = "".join(chr(c) for c in [
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
||||
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,
|
||||
0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
|
||||
0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
|
||||
0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
|
||||
0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
|
||||
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
|
||||
0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
|
||||
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
|
||||
0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
|
||||
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
|
||||
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
|
||||
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
|
||||
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
||||
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
|
||||
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
|
||||
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
|
||||
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
|
||||
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
|
||||
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
|
||||
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
|
||||
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
|
||||
])
|
||||
|
||||
#: IBM Codepage 437.
|
||||
IBMPC_MAP = "".join(chr(c) for c in [
|
||||
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
||||
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
|
||||
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
|
||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
||||
])
|
||||
|
||||
|
||||
#: VAX42 character set.
|
||||
VAX42_MAP = "".join(chr(c) for c in [
|
||||
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
||||
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
|
||||
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
|
||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
|
||||
0x0020, 0x043b, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x0435,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0441, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0435, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x043a,
|
||||
0x0070, 0x0071, 0x0442, 0x0073, 0x043b, 0x0435, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
||||
])
|
||||
|
||||
|
||||
MAPS = {
|
||||
b"B": LAT1_MAP,
|
||||
b"0": VT100_MAP,
|
||||
b"U": IBMPC_MAP,
|
||||
b"V": VAX42_MAP
|
||||
}
|
||||
26
pyte/compat.py
Normal file
26
pyte/compat.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.compat
|
||||
~~~~~~~~~~~
|
||||
|
||||
Python version specific compatibility fixes.
|
||||
|
||||
:copyright: (c) 2015-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
from future_builtins import map
|
||||
|
||||
range = xrange
|
||||
str = unicode
|
||||
chr = unichr
|
||||
|
||||
from functools import partial
|
||||
iter_bytes = partial(map, ord)
|
||||
else:
|
||||
from builtins import map, range, str, chr
|
||||
iter_bytes = iter
|
||||
70
pyte/control.py
Normal file
70
pyte/control.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.control
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module defines simple control sequences, recognized by
|
||||
:class:`~pyte.streams.Stream`, the set of codes here is for
|
||||
``TERM=linux`` which is a superset of VT102.
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
#: *Space*: Not suprisingly -- ``" "``.
|
||||
SP = b" "
|
||||
|
||||
#: *Null*: Does nothing.
|
||||
NUL = b"\x00"
|
||||
|
||||
#: *Bell*: Beeps.
|
||||
BEL = b"\x07"
|
||||
|
||||
#: *Backspace*: Backspace one column, but not past the begining of the
|
||||
#: line.
|
||||
BS = b"\x08"
|
||||
|
||||
#: *Horizontal tab*: Move cursor to the next tab stop, or to the end
|
||||
#: of the line if there is no earlier tab stop.
|
||||
HT = b"\x09"
|
||||
|
||||
#: *Linefeed*: Give a line feed, and, if :data:`pyte.modes.LNM` (new
|
||||
#: line mode) is set also a carriage return.
|
||||
LF = b"\n"
|
||||
#: *Vertical tab*: Same as :data:`LF`.
|
||||
VT = b"\x0b"
|
||||
#: *Form feed*: Same as :data:`LF`.
|
||||
FF = b"\x0c"
|
||||
|
||||
#: *Carriage return*: Move cursor to left margin on current line.
|
||||
CR = b"\r"
|
||||
|
||||
#: *Shift out*: Activate G1 character set.
|
||||
SO = b"\x0e"
|
||||
|
||||
#: *Shift in*: Activate G0 character set.
|
||||
SI = b"\x0f"
|
||||
|
||||
#: *Cancel*: Interrupt escape sequence. If received during an escape or
|
||||
#: control sequence, cancels the sequence and displays substitution
|
||||
#: character.
|
||||
CAN = b"\x18"
|
||||
#: *Substitute*: Same as :data:`CAN`.
|
||||
SUB = b"\x1a"
|
||||
|
||||
#: *Escape*: Starts an escape sequence.
|
||||
ESC = b"\x1b"
|
||||
|
||||
#: *Delete*: Is ignored.
|
||||
DEL = b"\x7f"
|
||||
|
||||
#: *Control sequence introducer*: An equivalent for ``ESC [``.
|
||||
CSI = b"\x9b"
|
||||
|
||||
#: *String terminator*.
|
||||
ST = b"\x9c"
|
||||
|
||||
#: *Operating system command*.
|
||||
OSC = b"\x9d"
|
||||
153
pyte/escape.py
Normal file
153
pyte/escape.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.escape
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module defines both CSI and non-CSI escape sequences, recognized
|
||||
by :class:`~pyte.streams.Stream` and subclasses.
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
#: *Reset*.
|
||||
RIS = b"c"
|
||||
|
||||
#: *Index*: Move cursor down one line in same column. If the cursor is
|
||||
#: at the bottom margin, the screen performs a scroll-up.
|
||||
IND = b"D"
|
||||
|
||||
#: *Next line*: Same as :data:`pyte.control.LF`.
|
||||
NEL = b"E"
|
||||
|
||||
#: Tabulation set: Set a horizontal tab stop at cursor position.
|
||||
HTS = b"H"
|
||||
|
||||
#: *Reverse index*: Move cursor up one line in same column. If the
|
||||
#: cursor is at the top margin, the screen performs a scroll-down.
|
||||
RI = b"M"
|
||||
|
||||
#: Save cursor: Save cursor position, character attribute (graphic
|
||||
#: rendition), character set, and origin mode selection (see
|
||||
#: :data:`DECRC`).
|
||||
DECSC = b"7"
|
||||
|
||||
#: *Restore cursor*: Restore previously saved cursor position, character
|
||||
#: attribute (graphic rendition), character set, and origin mode
|
||||
#: selection. If none were saved, move cursor to home position.
|
||||
DECRC = b"8"
|
||||
|
||||
# "Sharp" escape sequences.
|
||||
# -------------------------
|
||||
|
||||
#: *Alignment display*: Fill screen with uppercase E's for testing
|
||||
#: screen focus and alignment.
|
||||
DECALN = b"8"
|
||||
|
||||
|
||||
# ECMA-48 CSI sequences.
|
||||
# ---------------------
|
||||
|
||||
#: *Insert character*: Insert the indicated # of blank characters.
|
||||
ICH = b"@"
|
||||
|
||||
#: *Cursor up*: Move cursor up the indicated # of lines in same column.
|
||||
#: Cursor stops at top margin.
|
||||
CUU = b"A"
|
||||
|
||||
#: *Cursor down*: Move cursor down the indicated # of lines in same
|
||||
#: column. Cursor stops at bottom margin.
|
||||
CUD = b"B"
|
||||
|
||||
#: *Cursor forward*: Move cursor right the indicated # of columns.
|
||||
#: Cursor stops at right margin.
|
||||
CUF = b"C"
|
||||
|
||||
#: *Cursor back*: Move cursor left the indicated # of columns. Cursor
|
||||
#: stops at left margin.
|
||||
CUB = b"D"
|
||||
|
||||
#: *Cursor next line*: Move cursor down the indicated # of lines to
|
||||
#: column 1.
|
||||
CNL = b"E"
|
||||
|
||||
#: *Cursor previous line*: Move cursor up the indicated # of lines to
|
||||
#: column 1.
|
||||
CPL = b"F"
|
||||
|
||||
#: *Cursor horizontal align*: Move cursor to the indicated column in
|
||||
#: current line.
|
||||
CHA = b"G"
|
||||
|
||||
#: *Cursor position*: Move cursor to the indicated line, column (origin
|
||||
#: at ``1, 1``).
|
||||
CUP = b"H"
|
||||
|
||||
#: *Erase data* (default: from cursor to end of line).
|
||||
ED = b"J"
|
||||
|
||||
#: *Erase in line* (default: from cursor to end of line).
|
||||
EL = b"K"
|
||||
|
||||
#: *Insert line*: Insert the indicated # of blank lines, starting from
|
||||
#: the current line. Lines displayed below cursor move down. Lines moved
|
||||
#: past the bottom margin are lost.
|
||||
IL = b"L"
|
||||
|
||||
#: *Delete line*: Delete the indicated # of lines, starting from the
|
||||
#: current line. As lines are deleted, lines displayed below cursor
|
||||
#: move up. Lines added to bottom of screen have spaces with same
|
||||
#: character attributes as last line move up.
|
||||
DL = b"M"
|
||||
|
||||
#: *Delete character*: Delete the indicated # of characters on the
|
||||
#: current line. When character is deleted, all characters to the right
|
||||
#: of cursor move left.
|
||||
DCH = b"P"
|
||||
|
||||
#: *Erase character*: Erase the indicated # of characters on the
|
||||
#: current line.
|
||||
ECH = b"X"
|
||||
|
||||
#: *Horizontal position relative*: Same as :data:`CUF`.
|
||||
HPR = b"a"
|
||||
|
||||
#: *Device Attributes*.
|
||||
DA = b"c"
|
||||
|
||||
#: *Vertical position adjust*: Move cursor to the indicated line,
|
||||
#: current column.
|
||||
VPA = b"d"
|
||||
|
||||
#: *Vertical position relative*: Same as :data:`CUD`.
|
||||
VPR = b"e"
|
||||
|
||||
#: *Horizontal / Vertical position*: Same as :data:`CUP`.
|
||||
HVP = b"f"
|
||||
|
||||
#: *Tabulation clear*: Clears a horizontal tab stop at cursor position.
|
||||
TBC = b"g"
|
||||
|
||||
#: *Set mode*.
|
||||
SM = b"h"
|
||||
|
||||
#: *Reset mode*.
|
||||
RM = b"l"
|
||||
|
||||
#: *Select graphics rendition*: The terminal can display the following
|
||||
#: character attributes that change the character display without
|
||||
#: changing the character (see :mod:`pyte.graphics`).
|
||||
SGR = b"m"
|
||||
|
||||
#: *Device status report*.
|
||||
DSR = b"n"
|
||||
|
||||
#: *Select top and bottom margins*: Selects margins, defining the
|
||||
#: scrolling region; parameters are top and bottom line. If called
|
||||
#: without any arguments, whole screen is used.
|
||||
DECSTBM = b"r"
|
||||
|
||||
#: *Horizontal position adjust*: Same as :data:`CHA`.
|
||||
HPA = b"'"
|
||||
149
pyte/graphics.py
Normal file
149
pyte/graphics.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.graphics
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
This module defines graphic-related constants, mostly taken from
|
||||
:manpage:`console_codes(4)` and
|
||||
http://pueblo.sourceforge.net/doc/manual/ansi_color_codes.html.
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
#: A mapping of ANSI text style codes to style names, "+" means the:
|
||||
#: attribute is set, "-" -- reset; example:
|
||||
#:
|
||||
#: >>> text[1]
|
||||
#: '+bold'
|
||||
#: >>> text[9]
|
||||
#: '+strikethrough'
|
||||
TEXT = {
|
||||
1: "+bold" ,
|
||||
3: "+italics",
|
||||
4: "+underscore",
|
||||
7: "+reverse",
|
||||
9: "+strikethrough",
|
||||
22: "-bold",
|
||||
23: "-italics",
|
||||
24: "-underscore",
|
||||
27: "-reverse",
|
||||
29: "-strikethrough",
|
||||
}
|
||||
|
||||
#: A mapping of ANSI foreground color codes to color names.
|
||||
#:
|
||||
#: >>> FG_ANSI[30]
|
||||
#: 'black'
|
||||
#: >>> FG_ANSI[38]
|
||||
#: 'default'
|
||||
FG_ANSI = {
|
||||
30: "black",
|
||||
31: "red",
|
||||
32: "green",
|
||||
33: "brown",
|
||||
34: "blue",
|
||||
35: "magenta",
|
||||
36: "cyan",
|
||||
37: "white",
|
||||
39: "default" # white.
|
||||
}
|
||||
|
||||
#: An alias to :data:`~pyte.graphics.FG_ANSI` for compatibility.
|
||||
FG = FG_ANSI
|
||||
|
||||
#: A mapping of non-standard ``aixterm`` foreground color codes to
|
||||
#: color names. These are high intensity colors and thus should be
|
||||
#: complemented by ``+bold``.
|
||||
FG_AIXTERM = {
|
||||
90: "black",
|
||||
91: "red",
|
||||
92: "green",
|
||||
93: "brown",
|
||||
94: "blue",
|
||||
95: "magenta",
|
||||
96: "cyan",
|
||||
97: "white"
|
||||
}
|
||||
|
||||
#: A mapping of ANSI background color codes to color names.
|
||||
#:
|
||||
#: >>> BG_ANSI[40]
|
||||
#: 'black'
|
||||
#: >>> BG_ANSI[48]
|
||||
#: 'default'
|
||||
BG_ANSI = {
|
||||
40: "black",
|
||||
41: "red",
|
||||
42: "green",
|
||||
43: "brown",
|
||||
44: "blue",
|
||||
45: "magenta",
|
||||
46: "cyan",
|
||||
47: "white",
|
||||
49: "default" # black.
|
||||
}
|
||||
|
||||
#: An alias to :data:`~pyte.graphics.BG_ANSI` for compatibility.
|
||||
BG = BG_ANSI
|
||||
|
||||
#: A mapping of non-standard ``aixterm`` background color codes to
|
||||
#: color names. These are high intensity colors and thus should be
|
||||
#: complemented by ``+bold``.
|
||||
BG_AIXTERM = {
|
||||
100: "black",
|
||||
101: "red",
|
||||
102: "green",
|
||||
103: "brown",
|
||||
104: "blue",
|
||||
105: "magenta",
|
||||
106: "cyan",
|
||||
107: "white"
|
||||
}
|
||||
|
||||
#: SGR code for foreground in 256 or True color mode.
|
||||
FG_256 = 38
|
||||
|
||||
#: SGR code for background in 256 or True color mode.
|
||||
BG_256 = 48
|
||||
|
||||
#: A table of 256 foreground or background colors.
|
||||
# The following code is part of the Pygments project (BSD licensed).
|
||||
FG_BG_256 = [
|
||||
(0x00, 0x00, 0x00), # 0
|
||||
(0xcd, 0x00, 0x00), # 1
|
||||
(0x00, 0xcd, 0x00), # 2
|
||||
(0xcd, 0xcd, 0x00), # 3
|
||||
(0x00, 0x00, 0xee), # 4
|
||||
(0xcd, 0x00, 0xcd), # 5
|
||||
(0x00, 0xcd, 0xcd), # 6
|
||||
(0xe5, 0xe5, 0xe5), # 7
|
||||
(0x7f, 0x7f, 0x7f), # 8
|
||||
(0xff, 0x00, 0x00), # 9
|
||||
(0x00, 0xff, 0x00), # 10
|
||||
(0xff, 0xff, 0x00), # 11
|
||||
(0x5c, 0x5c, 0xff), # 12
|
||||
(0xff, 0x00, 0xff), # 13
|
||||
(0x00, 0xff, 0xff), # 14
|
||||
(0xff, 0xff, 0xff), # 15
|
||||
]
|
||||
|
||||
# colors 16..232: the 6x6x6 color cube
|
||||
valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
|
||||
|
||||
for i in range(217):
|
||||
r = valuerange[(i // 36) % 6]
|
||||
g = valuerange[(i // 6) % 6]
|
||||
b = valuerange[i % 6]
|
||||
FG_BG_256.append((r, g, b))
|
||||
|
||||
# colors 233..253: grayscale
|
||||
for i in range(1, 22):
|
||||
v = 8 + i * 10
|
||||
FG_BG_256.append((v, v, v))
|
||||
|
||||
FG_BG_256 = ["{0:02x}{1:02x}{2:02x}".format(r, g, b) for r, g, b in FG_BG_256]
|
||||
60
pyte/modes.py
Normal file
60
pyte/modes.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.modes
|
||||
~~~~~~~~~~
|
||||
|
||||
This module defines terminal mode switches, used by
|
||||
:class:`~pyte.screens.Screen`. There're two types of terminal modes:
|
||||
|
||||
* `non-private` which should be set with ``ESC [ N h``, where ``N``
|
||||
is an integer, representing mode being set; and
|
||||
* `private` which should be set with ``ESC [ ? N h``.
|
||||
|
||||
The latter are shifted 5 times to the right, to be easily
|
||||
distinguishable from the former ones; for example `Origin Mode`
|
||||
-- :data:`DECOM` is ``192`` not ``6``.
|
||||
|
||||
>>> DECOM
|
||||
192
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
#: *Line Feed/New Line Mode*: When enabled, causes a received
|
||||
#: :data:`~pyte.control.LF`, :data:`pyte.control.FF`, or
|
||||
#: :data:`~pyte.control.VT` to move the cursor to the first column of
|
||||
#: the next line.
|
||||
LNM = 20
|
||||
|
||||
#: *Insert/Replace Mode*: When enabled, new display characters move
|
||||
#: old display characters to the right. Characters moved past the
|
||||
#: right margin are lost. Otherwise, new display characters replace
|
||||
#: old display characters at the cursor position.
|
||||
IRM = 4
|
||||
|
||||
|
||||
# Private modes.
|
||||
# ..............
|
||||
|
||||
#: *Text Cursor Enable Mode*: determines if the text cursor is
|
||||
#: visible.
|
||||
DECTCEM = 25 << 5
|
||||
|
||||
#: *Screen Mode*: toggles screen-wide reverse-video mode.
|
||||
DECSCNM = 5 << 5
|
||||
|
||||
#: *Origin Mode*: allows cursor addressing relative to a user-defined
|
||||
#: origin. This mode resets when the terminal is powered up or reset.
|
||||
#: It does not affect the erase in display (ED) function.
|
||||
DECOM = 6 << 5
|
||||
|
||||
#: *Auto Wrap Mode*: selects where received graphic characters appear
|
||||
#: when the cursor is at the right margin.
|
||||
DECAWM = 7 << 5
|
||||
|
||||
#: *Column Mode*: selects the number of columns per line (80 or 132)
|
||||
#: on the screen.
|
||||
DECCOLM = 3 << 5
|
||||
1273
pyte/screens.py
Normal file
1273
pyte/screens.py
Normal file
File diff suppressed because it is too large
Load diff
410
pyte/streams.py
Normal file
410
pyte/streams.py
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyte.streams
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module provides three stream implementations with different
|
||||
features; for starters, here's a quick example of how streams are
|
||||
typically used:
|
||||
|
||||
>>> import pyte
|
||||
>>> screen = pyte.Screen(80, 24)
|
||||
>>> stream = pyte.Stream(screen)
|
||||
>>> stream.feed(b"\x1B[5B") # Move the cursor down 5 rows.
|
||||
>>> screen.cursor.y
|
||||
5
|
||||
|
||||
:copyright: (c) 2011-2012 by Selectel.
|
||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
||||
see AUTHORS for details.
|
||||
:license: LGPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
|
||||
from . import control as ctrl, escape as esc
|
||||
from .compat import str
|
||||
|
||||
|
||||
class Stream(object):
|
||||
"""A stream is a state machine that parses a stream of bytes and
|
||||
dispatches events based on what it sees.
|
||||
|
||||
:param pyte.screens.Screen screen: a screen to dispatch events to.
|
||||
:param bool strict: check if a given screen implements all required
|
||||
events.
|
||||
|
||||
.. note::
|
||||
|
||||
Stream only accepts :func:`bytes` as input. Decoding it into text
|
||||
is the responsibility of the :class:`~pyte.screens.Screen`.
|
||||
|
||||
.. versionchanged 0.6.0::
|
||||
|
||||
For performance reasons the binding between stream events and
|
||||
screen methods was made static. As a result, the stream **will
|
||||
not** dispatch events to methods added to screen **after** the
|
||||
stream was created.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`man console_codes <http://linux.die.net/man/4/console_codes>`_
|
||||
For details on console codes listed bellow in :attr:`basic`,
|
||||
:attr:`escape`, :attr:`csi`, :attr:`sharp` and :attr:`percent`.
|
||||
"""
|
||||
|
||||
#: Control sequences, which don't require any arguments.
|
||||
basic = {
|
||||
ctrl.BEL: "bell",
|
||||
ctrl.BS: "backspace",
|
||||
ctrl.HT: "tab",
|
||||
ctrl.LF: "linefeed",
|
||||
ctrl.VT: "linefeed",
|
||||
ctrl.FF: "linefeed",
|
||||
ctrl.CR: "carriage_return",
|
||||
ctrl.SO: "shift_out",
|
||||
ctrl.SI: "shift_in",
|
||||
}
|
||||
|
||||
#: non-CSI escape sequences.
|
||||
escape = {
|
||||
esc.RIS: "reset",
|
||||
esc.IND: "index",
|
||||
esc.NEL: "linefeed",
|
||||
esc.RI: "reverse_index",
|
||||
esc.HTS: "set_tab_stop",
|
||||
esc.DECSC: "save_cursor",
|
||||
esc.DECRC: "restore_cursor",
|
||||
}
|
||||
|
||||
#: "sharp" escape sequences -- ``ESC # <N>``.
|
||||
sharp = {
|
||||
esc.DECALN: "alignment_display",
|
||||
}
|
||||
|
||||
#: CSI escape sequences -- ``CSI P1;P2;...;Pn <fn>``.
|
||||
csi = {
|
||||
esc.ICH: "insert_characters",
|
||||
esc.CUU: "cursor_up",
|
||||
esc.CUD: "cursor_down",
|
||||
esc.CUF: "cursor_forward",
|
||||
esc.CUB: "cursor_back",
|
||||
esc.CNL: "cursor_down1",
|
||||
esc.CPL: "cursor_up1",
|
||||
esc.CHA: "cursor_to_column",
|
||||
esc.CUP: "cursor_position",
|
||||
esc.ED: "erase_in_display",
|
||||
esc.EL: "erase_in_line",
|
||||
esc.IL: "insert_lines",
|
||||
esc.DL: "delete_lines",
|
||||
esc.DCH: "delete_characters",
|
||||
esc.ECH: "erase_characters",
|
||||
esc.HPR: "cursor_forward",
|
||||
esc.DA: "report_device_attributes",
|
||||
esc.VPA: "cursor_to_line",
|
||||
esc.VPR: "cursor_down",
|
||||
esc.HVP: "cursor_position",
|
||||
esc.TBC: "clear_tab_stop",
|
||||
esc.SM: "set_mode",
|
||||
esc.RM: "reset_mode",
|
||||
esc.SGR: "select_graphic_rendition",
|
||||
esc.DSR: "report_device_status",
|
||||
esc.DECSTBM: "set_margins",
|
||||
esc.HPA: "cursor_to_column"
|
||||
}
|
||||
|
||||
#: A set of all events dispatched by the stream.
|
||||
events = frozenset(itertools.chain(
|
||||
basic.values(), escape.values(), sharp.values(), csi.values(),
|
||||
["define_charset", "select_other_charset"],
|
||||
["set_icon", "set_title"], # OSC.
|
||||
["draw", "debug"]))
|
||||
|
||||
#: A regular expression pattern matching everything what can be
|
||||
#: considered plain text.
|
||||
_special = set([ctrl.ESC, ctrl.CSI, ctrl.NUL, ctrl.DEL, ctrl.OSC])
|
||||
_special.update(basic)
|
||||
_text_pattern = re.compile(
|
||||
b"[^" + b"".join(map(re.escape, _special)) + b"]+")
|
||||
del _special
|
||||
|
||||
def __init__(self, screen=None, strict=True):
|
||||
self.listener = None
|
||||
self.strict = False
|
||||
|
||||
if screen is not None:
|
||||
self.attach(screen)
|
||||
|
||||
def attach(self, screen, only=()):
|
||||
"""Adds a given screen to the listener queue.
|
||||
|
||||
:param pyte.screens.Screen screen: a screen to attach to.
|
||||
:param list only: a list of events you want to dispatch to a
|
||||
given screen (empty by default, which means
|
||||
-- dispatch all events).
|
||||
"""
|
||||
if self.strict:
|
||||
for event in self.events:
|
||||
if not hasattr(screen, event):
|
||||
error_message = "{0} is missing {1}".format(screen, event)
|
||||
raise TypeError(error_message)
|
||||
if self.listener is not None:
|
||||
warnings.warn("As of version 0.6.0 the listener queue is "
|
||||
"restricted to a single element. Existing "
|
||||
"listener {0} will be replaced."
|
||||
.format(self.listener), DeprecationWarning)
|
||||
|
||||
self.listener = screen
|
||||
self._parser = self._parser_fsm()
|
||||
self._taking_plain_text = next(self._parser)
|
||||
|
||||
def detach(self, screen):
|
||||
"""Removes a given screen from the listener queue and fails
|
||||
silently if it's not attached.
|
||||
|
||||
:param pyte.screens.Screen screen: a screen to detach.
|
||||
"""
|
||||
if screen is self.listener:
|
||||
self.listener = None
|
||||
|
||||
def feed(self, data):
|
||||
"""Consumes a string and advances the state as necessary.
|
||||
|
||||
:param bytes data: a blob of data to feed from.
|
||||
"""
|
||||
if isinstance(data, str):
|
||||
warnings.warn("As of version 0.6.0 ``pyte.streams.Stream.feed``"
|
||||
"requires input in bytes. This warnings will become "
|
||||
"and error in 0.6.1.")
|
||||
data = data.encode("utf-8")
|
||||
elif not isinstance(data, bytes):
|
||||
raise TypeError("{0} requires bytes input"
|
||||
.format(self.__class__.__name__))
|
||||
|
||||
send = self._parser.send
|
||||
draw = self.listener.draw
|
||||
match_text = self._text_pattern.match
|
||||
taking_plain_text = self._taking_plain_text
|
||||
|
||||
# TODO: use memoryview?
|
||||
length = len(data)
|
||||
offset = 0
|
||||
while offset < length:
|
||||
if taking_plain_text:
|
||||
match = match_text(data, offset)
|
||||
if match:
|
||||
start, offset = match.span()
|
||||
draw(data[start:offset])
|
||||
else:
|
||||
taking_plain_text = False
|
||||
else:
|
||||
taking_plain_text = send(data[offset:offset + 1])
|
||||
offset += 1
|
||||
|
||||
self._taking_plain_text = taking_plain_text
|
||||
|
||||
def _parser_fsm(self):
|
||||
"""An FSM implemented as a coroutine.
|
||||
|
||||
This generator is not the most beautiful, but it is as performant
|
||||
as possible. When a process generates a lot of output, then this
|
||||
will be the bottleneck, because it processes just one character
|
||||
at a time.
|
||||
|
||||
We did many manual optimizations to this function in order to make
|
||||
it as efficient as possible. Don't change anything without profiling
|
||||
first.
|
||||
"""
|
||||
basic = self.basic
|
||||
listener = self.listener
|
||||
draw = listener.draw
|
||||
debug = listener.debug
|
||||
|
||||
ESC, CSI = ctrl.ESC, ctrl.CSI
|
||||
OSC, ST = ctrl.OSC, ctrl.ST
|
||||
SP_OR_GT = ctrl.SP + b">"
|
||||
NUL_OR_DEL = ctrl.NUL + ctrl.DEL
|
||||
CAN_OR_SUB = ctrl.CAN + ctrl.SUB
|
||||
ALLOWED_IN_CSI = b"".join([ctrl.BEL, ctrl.BS, ctrl.HT, ctrl.LF,
|
||||
ctrl.VT, ctrl.FF, ctrl.CR])
|
||||
|
||||
def create_dispatcher(mapping):
|
||||
return defaultdict(lambda: debug, dict(
|
||||
(event, getattr(listener, attr))
|
||||
for event, attr in mapping.items()))
|
||||
|
||||
basic_dispatch = create_dispatcher(basic)
|
||||
sharp_dispatch = create_dispatcher(self.sharp)
|
||||
escape_dispatch = create_dispatcher(self.escape)
|
||||
csi_dispatch = create_dispatcher(self.csi)
|
||||
|
||||
while True:
|
||||
# ``True`` tells ``Screen.feed`` that it is allowed to send
|
||||
# chunks of plain text directly to the listener, instead
|
||||
# of this generator.)
|
||||
char = yield True
|
||||
|
||||
if char == ESC:
|
||||
# Most non-VT52 commands start with a left-bracket after the
|
||||
# escape and then a stream of parameters and a command; with
|
||||
# a single notable exception -- :data:`escape.DECOM` sequence,
|
||||
# which starts with a sharp.
|
||||
#
|
||||
# .. versionchanged:: 0.4.10
|
||||
#
|
||||
# For compatibility with Linux terminal stream also
|
||||
# recognizes ``ESC % C`` sequences for selecting control
|
||||
# character set. However, in the current version these
|
||||
# are noop.
|
||||
char = yield
|
||||
if char == b"[":
|
||||
char = CSI # Go to CSI.
|
||||
elif char == b"]":
|
||||
char = OSC # Go to OSC.
|
||||
else:
|
||||
if char == b"#":
|
||||
sharp_dispatch[(yield)]()
|
||||
if char == b"%":
|
||||
listener.select_other_charset((yield))
|
||||
elif char in b"()":
|
||||
listener.define_charset((yield), mode=char)
|
||||
else:
|
||||
escape_dispatch[char]()
|
||||
continue # Don't go to CSI.
|
||||
|
||||
if char in basic:
|
||||
basic_dispatch[char]()
|
||||
elif char == CSI:
|
||||
# All parameters are unsigned, positive decimal integers, with
|
||||
# the most significant digit sent first. Any parameter greater
|
||||
# than 9999 is set to 9999. If you do not specify a value, a 0
|
||||
# value is assumed.
|
||||
#
|
||||
# .. seealso::
|
||||
#
|
||||
# `VT102 User Guide <http://vt100.net/docs/vt102-ug/>`_
|
||||
# For details on the formatting of escape arguments.
|
||||
#
|
||||
# `VT220 Programmer Ref. <http://vt100.net/docs/vt220-rm/>`_
|
||||
# For details on the characters valid for use as
|
||||
# arguments.
|
||||
params = []
|
||||
current = bytearray()
|
||||
private = False
|
||||
while True:
|
||||
char = yield
|
||||
if char == b"?":
|
||||
private = True
|
||||
elif char in ALLOWED_IN_CSI:
|
||||
basic_dispatch[char]()
|
||||
elif char in SP_OR_GT:
|
||||
# We don't handle secondary DA atm.
|
||||
pass
|
||||
elif char in CAN_OR_SUB:
|
||||
# If CAN or SUB is received during a sequence, the
|
||||
# current sequence is aborted; terminal displays
|
||||
# the substitute character, followed by characters
|
||||
# in the sequence received after CAN or SUB.
|
||||
draw(char)
|
||||
break
|
||||
elif char.isdigit():
|
||||
current.extend(char)
|
||||
else:
|
||||
params.append(min(int(bytes(current) or 0), 9999))
|
||||
|
||||
if char == b";":
|
||||
current = bytearray()
|
||||
else:
|
||||
if private:
|
||||
csi_dispatch[char](*params, private=True)
|
||||
else:
|
||||
csi_dispatch[char](*params)
|
||||
break # CSI is finished.
|
||||
elif char == OSC:
|
||||
code = yield
|
||||
param = bytearray()
|
||||
while True:
|
||||
char = yield
|
||||
if char == ST or char == ctrl.BEL:
|
||||
break
|
||||
else:
|
||||
param.extend(char)
|
||||
|
||||
param = bytes(param[1:]) # Drop the ;.
|
||||
if code in b"01":
|
||||
listener.set_icon_name(param)
|
||||
if code in b"02":
|
||||
listener.set_title(param)
|
||||
elif char not in NUL_OR_DEL:
|
||||
draw(char)
|
||||
|
||||
|
||||
class ByteStream(Stream):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("As of version 0.6.0 ``pyte.streams.ByteStream`` is an "
|
||||
"alias for ``pyte.streams.Stream``. The former will be "
|
||||
"removed in pyte 0.6.1.", DeprecationWarning)
|
||||
|
||||
if kwargs.pop("encodings", None):
|
||||
warnings.warn(
|
||||
"As of version 0.6.0 ``pyte.streams.ByteStream`` no longer "
|
||||
"decodes input.", DeprecationWarning)
|
||||
|
||||
super(ByteStream, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class DebugStream(Stream):
|
||||
r"""Stream, which dumps a subset of the dispatched events to a given
|
||||
file-like object (:data:`sys.stdout` by default).
|
||||
|
||||
>>> import io
|
||||
>>> with io.StringIO() as buf:
|
||||
... stream = DebugStream(to=buf)
|
||||
... stream.feed(b"\x1b[1;24r\x1b[4l\x1b[24;1H\x1b[0;10m")
|
||||
... print(buf.getvalue())
|
||||
...
|
||||
... # doctest: +NORMALIZE_WHITESPACE
|
||||
SET_MARGINS 1; 24
|
||||
RESET_MODE 4
|
||||
CURSOR_POSITION 24; 1
|
||||
SELECT_GRAPHIC_RENDITION 0; 10
|
||||
|
||||
:param file to: a file-like object to write debug information to.
|
||||
:param list only: a list of events you want to debug (empty by
|
||||
default, which means -- debug all events).
|
||||
"""
|
||||
|
||||
def __init__(self, to=sys.stdout, only=(), *args, **kwargs):
|
||||
def safe_str(chunk):
|
||||
if isinstance(chunk, bytes):
|
||||
chunk = chunk.decode("utf-8")
|
||||
elif not isinstance(chunk, str):
|
||||
chunk = str(chunk)
|
||||
|
||||
return chunk
|
||||
|
||||
def noop(*args, **kwargs):
|
||||
pass
|
||||
|
||||
class Bugger(object):
|
||||
def __getattr__(self, event):
|
||||
if only and event not in only:
|
||||
return noop
|
||||
|
||||
def inner(*args, **kwargs):
|
||||
to.write(event.upper() + " ")
|
||||
to.write("; ".join(map(safe_str, args)))
|
||||
to.write(" ")
|
||||
to.write(", ".join("{0}: {1}".format(k, safe_str(v))
|
||||
for k, v in kwargs.items()))
|
||||
to.write(os.linesep)
|
||||
return inner
|
||||
|
||||
super(DebugStream, self).__init__(Bugger(), *args, **kwargs)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
" Scan the following dirs recursively for tags
|
||||
let g:project_tags_dirs = ['kitty']
|
||||
let g:project_tags_dirs = ['kitty', 'pyte']
|
||||
let g:syntastic_python_flake8_exec = 'flake8'
|
||||
let g:ycm_python_binary_path = 'python3'
|
||||
set wildignore+==template.py
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue