Use the new native screen class

This commit is contained in:
Kovid Goyal 2016-11-18 15:30:59 +05:30
parent 6c39b07552
commit 3ce2c0553f
20 changed files with 75 additions and 2388 deletions

View File

@ -9,19 +9,20 @@ import select
import subprocess
import struct
from itertools import repeat
from functools import partial
from time import monotonic
from threading import Thread, current_thread
from queue import Queue, Empty
import glfw
from pyte.streams import Stream, DebugStream
from .constants import appname
from .char_grid import CharGrid
from .keys import interpret_text_event, interpret_key_event
from .screen import Screen
from .utils import resize_pty, create_pty, sanitize_title
from .fast_data_types import BRACKETED_PASTE_START, BRACKETED_PASTE_END
from .fast_data_types import (
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, parse_bytes_dump, parse_bytes
)
def handle_unix_signals():
@ -53,10 +54,9 @@ class Boss(Thread):
self.queue_action(self.initialize)
self.profile = args.profile
self.window, self.opts = window, opts
self.screen = Screen(self.opts.scrollback_lines, self)
self.screen = Screen(self)
self.char_grid = CharGrid(self.screen, opts, window_width, window_height)
sclass = DebugStream if args.dump_commands else Stream
self.stream = sclass(self.screen)
self.parse_bytes = partial(parse_bytes_dump, print) if args.dump_commands else parse_bytes
self.write_buf = memoryview(b'')
glfw.glfwSetCharModsCallback(window, self.on_text_input)
glfw.glfwSetKeyCallback(window, self.on_key)
@ -181,7 +181,7 @@ class Boss(Thread):
if self.pending_update_screen is not None:
if monotonic() > self.pending_update_screen:
self.apply_update_screen()
elif self.screen.is_dirty:
elif self.screen.is_dirty():
self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY
def close(self):
@ -209,7 +209,7 @@ class Boss(Thread):
except EnvironmentError:
data = b''
if data:
self.stream.feed(data)
self.parse_bytes(self.screen, data)
else: # EOF
self.shutdown()

View File

@ -24,7 +24,7 @@
// of the line if there is no earlier tab stop.
#define HT 0x09
// *Linefeed*: Give a line feed, and, if :data:`pyte.modes.LNM` (new
// *Linefeed*: Give a line feed, and, if LNM (new
// line mode) is set also a carriage return.
#define LF 10
@ -80,7 +80,7 @@
// at the bottom margin, the screen performs a scroll-up.
#define IND 'D'
// *Next line*: Same as :data:`pyte.control.LF`.
// *Next line*: Same as LF.
#define NEL 'E'
// Tabulation set: Set a horizontal tab stop at cursor position.
@ -197,7 +197,7 @@
// *Select graphics rendition*: The terminal can display the following
// character attributes that change the character display without
// changing the character (see :mod:`pyte.graphics`).
// changing the character
#define SGR 'm'
// *Device status report*.

View File

@ -504,7 +504,7 @@ parse_bytes(PyObject UNUSED *self, PyObject *args) {
Py_buffer pybuf;
Screen *screen;
#ifdef DUMP_COMMANDS
if (!PyArg_ParseTuple(args, "O!y*O", &Screen_Type, &screen, &pybuf, &dump_callback)) return NULL;
if (!PyArg_ParseTuple(args, "OO!y*", &dump_callback, &Screen_Type, &screen, &pybuf)) return NULL;
if (!PyCallable_Check(dump_callback)) { PyErr_SetString(PyExc_TypeError, "The dump callback must be a callable object"); return NULL; }
#else
if (!PyArg_ParseTuple(args, "O!y*", &Screen_Type, &screen, &pybuf)) return NULL;

View File

@ -18,7 +18,7 @@ static PyObject*
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
Screen *self;
PyObject *callbacks = Py_None;
unsigned int columns, lines;
unsigned int columns=80, lines=24;
if (!PyArg_ParseTuple(args, "|OII", &callbacks, &lines, &columns)) return NULL;
self = (Screen *)type->tp_alloc(type, 0);
@ -102,6 +102,12 @@ static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns
return true;
}
static bool screen_change_scrollback_size(Screen UNUSED *self, unsigned int UNUSED size) {
// TODO: Implement this
return true;
}
static void
dealloc(Screen* self) {
Py_CLEAR(self->callbacks);
@ -980,6 +986,36 @@ resize(Screen *self, PyObject *args) {
Py_RETURN_NONE;
}
static PyObject*
change_scrollback_size(Screen *self, PyObject *args) {
unsigned int count = 1;
if (!PyArg_ParseTuple(args, "|I", &count)) return NULL;
if (!screen_change_scrollback_size(self, MAX(100, count))) return NULL;
Py_RETURN_NONE;
}
static PyObject*
screen_update_cell_data(Screen *self, PyObject *args) {
SpriteMap *spm;
ColorProfile *color_profile;
PyObject *dp;
unsigned int *data;
unsigned long default_bg, default_fg;
int force_screen_refresh;
if (!PyArg_ParseTuple(args, "O!O!O!kkp", &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
data = PyLong_AsVoidPtr(dp);
PyObject *cursor_changed = self->change_tracker->cursor_changed ? Py_True : Py_False;
if (!tracker_update_cell_data(self->change_tracker, self->linebuf, spm, color_profile, data, default_fg, default_bg, (bool)force_screen_refresh)) return NULL;
Py_INCREF(cursor_changed);
return cursor_changed;
}
static PyObject* is_dirty(Screen *self) {
PyObject *ans = self->change_tracker->dirty ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
#define COUNT_WRAP(name) \
static PyObject* name(Screen *self, PyObject *args) { \
unsigned int count = 1; \
@ -1015,6 +1051,7 @@ static PyMethodDef methods[] = {
MND(delete_lines, METH_VARARGS)
MND(insert_characters, METH_VARARGS)
MND(delete_characters, METH_VARARGS)
MND(change_scrollback_size, METH_VARARGS)
MND(erase_characters, METH_VARARGS)
MND(cursor_up, METH_VARARGS)
MND(cursor_up1, METH_VARARGS)
@ -1023,7 +1060,9 @@ static PyMethodDef methods[] = {
MND(cursor_forward, METH_VARARGS)
MND(index, METH_NOARGS)
MND(reverse_index, METH_NOARGS)
MND(is_dirty, METH_NOARGS)
MND(resize, METH_VARARGS)
{"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""},
{NULL} /* Sentinel */
};

File diff suppressed because it is too large Load Diff

View File

@ -97,25 +97,15 @@ update_cell_range(ChangeTracker *self, PyObject *args) {
Py_RETURN_NONE;
}
static PyObject*
update_cell_data(ChangeTracker *self, PyObject *args) {
#define update_cell_data_doc "update_cell_data(line_buf, sprite_map, color_profile, data_ptr, default_fg, default_bg, force_screen_refresh)"
SpriteMap *spm;
LineBuf *lb;
ColorProfile *color_profile;
PyObject *dp;
unsigned int *data, y;
unsigned long default_bg, default_fg;
bool tracker_update_cell_data(ChangeTracker *self, LineBuf *lb, SpriteMap *spm, ColorProfile *color_profile, unsigned int *data, unsigned long default_fg, unsigned long default_bg, bool force_screen_refresh) {
unsigned int y;
Py_ssize_t start;
int force_screen_refresh;
if (!PyArg_ParseTuple(args, "O!O!O!O!kkp", &LineBuf_Type, &lb, &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
data = PyLong_AsVoidPtr(dp);
default_fg &= COL_MASK;
default_bg &= COL_MASK;
#define UPDATE_RANGE(xstart, xmax) \
linebuf_init_line(lb, y); \
if (!update_cell_range_data(spm, lb->line, (xstart), (xmax), color_profile, default_bg, default_fg, data)) return NULL;
if (!update_cell_range_data(spm, lb->line, (xstart), (xmax), color_profile, default_bg, default_fg, data)) return false;
if (self->screen_changed || force_screen_refresh) {
for (y = 0; y < self->ynum; y++) {
@ -146,9 +136,24 @@ update_cell_data(ChangeTracker *self, PyObject *args) {
}
}
}
PyObject *cursor_changed = self->cursor_changed ? Py_True : Py_False;
tracker_reset(self);
return true;
}
PyObject*
update_cell_data(ChangeTracker *self, PyObject *args) {
#define update_cell_data_doc "update_cell_data(line_buf, sprite_map, color_profile, data_ptr, default_fg, default_bg, force_screen_refresh)"
SpriteMap *spm;
LineBuf *lb;
ColorProfile *color_profile;
PyObject *dp;
unsigned int *data;
unsigned long default_bg, default_fg;
int force_screen_refresh;
if (!PyArg_ParseTuple(args, "O!O!O!O!kkp", &LineBuf_Type, &lb, &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
data = PyLong_AsVoidPtr(dp);
PyObject *cursor_changed = self->cursor_changed ? Py_True : Py_False;
if (!tracker_update_cell_data(self, lb, spm, color_profile, data, default_fg, default_bg, (bool)force_screen_refresh)) return NULL;
Py_INCREF(cursor_changed);
return cursor_changed;
}

View File

@ -52,3 +52,4 @@ static inline void tracker_reset(ChangeTracker *self) {
PyObject* tracker_consolidate_changes(ChangeTracker *self);
bool tracker_resize(ChangeTracker *self, unsigned int ynum, unsigned int xnum);
bool tracker_update_cell_data(ChangeTracker *, LineBuf *, SpriteMap *, ColorProfile *, unsigned int *, unsigned long, unsigned long, bool);

View File

@ -41,7 +41,7 @@ class TestScreen(BaseTest):
cd = CmdDump()
if isinstance(x, str):
x = x.encode('utf-8')
parse_bytes_dump(s, x, cd)
parse_bytes_dump(cd, s, x)
self.ae(tuple(cd), cmds)
def test_simple_parsing(self):

View File

@ -1,14 +0,0 @@
Authors
=======
- George Shuklin
- Sergei Lebedev
Contributors
------------
- Alexey Shamrin
- Steve Cohen
- Jonathan Slenders
- David O'Shea
- Andreas Stührk

View File

@ -1,165 +0,0 @@
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.

View File

@ -1,52 +0,0 @@
# -*- 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", "DebugStream")
import io
from .streams import Stream, 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())

View File

@ -1,33 +0,0 @@
# -*- 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:]))

View File

@ -1,143 +0,0 @@
# -*- 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
}

View File

@ -1,26 +0,0 @@
# -*- 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

View File

@ -1,73 +0,0 @@
# -*- 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"
#: Device Control function (DCS)
DCS = ESC + b"P"

View File

@ -1,165 +0,0 @@
# -*- 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"
#: Set normal keypad mode
DECPNM = b'>'
#: Set alternate keypad mode
DECPAM = b'='
# "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"'"
# Misc sequences
#: Change cursor shape/blink
DECSCUSR = b'q'

View File

@ -1,160 +0,0 @@
# -*- 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",
}
DISPLAY = {
1: ("bold", True),
3: ("italic", True),
4: ("decoration", 1),
7: ("reverse", True),
9: ("strikethrough", True),
22: ("bold", False),
23: ("italic", False),
24: ("decoration", 0),
27: ("reverse", False),
29: ("strikethrough", False),
}
#: 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))

View File

@ -1,77 +0,0 @@
# -*- 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
#: Bracketed paste mode
# http://cirw.in/blog/bracketed-paste
BRACKETED_PASTE = 2004 << 5
BRACKETED_PASTE_START = '\033[200~'.encode('ascii')
BRACKETED_PASTE_END = '\033[201~'.encode('ascii')
#: Alternate screen buffer
ALTERNATE_SCREEN = 1049 << 5
#: Xterm mouse protocol
SEND_MOUSE_ON_PRESS_AND_RELEASE = 1000 << 5
HILITE_MOUSE_TRACKING = 1001 << 5
CELL_MOTION_MOUSE_TRACKING = 1002 << 5
FOCUS_TRACKING = 1004 << 5
UTF8_MOUSE_MODE = 1005 << 5
SGR_MOUSE_MODE = 1006 << 6

View File

@ -1,419 +0,0 @@
# -*- 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
from functools import wraps
import itertools
import os
import re
import sys
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",
esc.DECPNM: 'normal_keypad_mode',
esc.DECPAM: 'alternate_keypad_mode',
}
#: "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",
esc.DECSCUSR: 'set_cursor_shape',
}
#: 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", 'set_dynamic_color'], # 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)
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: bytes) -> None:
"""Consumes a string and advances the state as necessary.
:param bytes data: a blob of data to feed from.
"""
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 is not None:
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, DCS = ctrl.OSC, ctrl.ST, ctrl.DCS
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.
elif char == b'P':
char = DCS # Go to DCS
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 = secondary = 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:
secondary = char.decode('ascii') # Added by Kovid
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:
if secondary: # Added by Kovid
csi_dispatch[char](*params, secondary=secondary)
else:
csi_dispatch[char](*params)
break # CSI is finished.
elif char == OSC:
code = bytearray()
while True:
char = yield
if char == ST or char == ctrl.BEL or char == b';':
break
code.extend(char)
code = bytes(code)
param = bytearray()
if char == b';':
while True:
char = yield
if char == ST or char == ctrl.BEL:
break
else:
param.extend(char)
param = bytes(param)
try:
code = int(code)
except Exception:
code = None
if code == 0:
listener.set_title(param)
listener.set_icon_name(param)
elif code == 1:
listener.set_icon_name(param)
elif code == 2:
listener.set_title(param)
elif 9 < code < 20:
listener.set_dynamic_color(code, param)
elif 109 < code < 120:
listener.set_dynamic_color(code)
elif char == DCS:
# See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Device-Control-functions
code = yield
param = bytearray()
while True:
char = yield
if char == ST:
break
else:
param.extend(char)
# TODO: Implement these
elif char not in NUL_OR_DEL:
draw(char)
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, screen, to=sys.stdout, only=()):
stream = super(DebugStream, self)
def safe_str(chunk):
if isinstance(chunk, bytes):
chunk = chunk.decode("utf-8")
elif not isinstance(chunk, str):
chunk = str(chunk)
return chunk
class Bugger:
def __getattr__(self, event):
@wraps(getattr(screen, event))
def inner(*args, **kwargs):
if not only or event in only:
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)
getattr(screen, event)(*args, **kwargs)
return inner
stream.__init__(Bugger())

View File

@ -1,5 +1,5 @@
" Scan the following dirs recursively for tags
let g:project_tags_dirs = ['kitty', 'pyte']
let g:project_tags_dirs = ['kitty']
let g:syntastic_python_flake8_exec = 'flake8'
let g:ycm_python_binary_path = 'python3'
set wildignore+==template.py