Kittens learn about bracketed paste
This commit is contained in:
parent
13c034c613
commit
f4f0b8fd5f
@ -56,7 +56,7 @@ def sanitize_term(output_fd):
|
|||||||
|
|
||||||
class Loop:
|
class Loop:
|
||||||
|
|
||||||
def __init__(self, input_fd=None, output_fd=None):
|
def __init__(self, input_fd=None, output_fd=None, sanitize_bracketed_paste='[\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
||||||
self.input_fd = input_fd or sys.stdin.fileno()
|
self.input_fd = input_fd or sys.stdin.fileno()
|
||||||
self.output_fd = output_fd or sys.stdout.fileno()
|
self.output_fd = output_fd or sys.stdout.fileno()
|
||||||
self.wakeup_read_fd, self.wakeup_write_fd = os.pipe()
|
self.wakeup_read_fd, self.wakeup_write_fd = os.pipe()
|
||||||
@ -76,6 +76,10 @@ class Loop:
|
|||||||
self.iov_limit = 255
|
self.iov_limit = 255
|
||||||
self.parse_input_from_terminal = partial(parse_input_from_terminal, self.on_text, self.on_dcs, self.on_csi, self.on_osc, self.on_pm, self.on_apc)
|
self.parse_input_from_terminal = partial(parse_input_from_terminal, self.on_text, self.on_dcs, self.on_csi, self.on_osc, self.on_pm, self.on_apc)
|
||||||
self.ebs_pat = re.compile('([\177\r])')
|
self.ebs_pat = re.compile('([\177\r])')
|
||||||
|
self.in_bracketed_paste = False
|
||||||
|
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
|
||||||
|
if self.sanitize_bracketed_paste:
|
||||||
|
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
|
||||||
|
|
||||||
def _read_ready(self, handler):
|
def _read_ready(self, handler):
|
||||||
if not self.read_allowed:
|
if not self.read_allowed:
|
||||||
@ -89,24 +93,34 @@ class Loop:
|
|||||||
self.read_buf = data
|
self.read_buf = data
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
try:
|
try:
|
||||||
self.read_buf = self.parse_input_from_terminal(self.read_buf)
|
self.read_buf = self.parse_input_from_terminal(self.read_buf, self.in_bracketed_paste)
|
||||||
finally:
|
finally:
|
||||||
del self.handler
|
del self.handler
|
||||||
|
|
||||||
def on_text(self, text):
|
def on_text(self, text):
|
||||||
|
if self.in_bracketed_paste and self.sanitize_bracketed_paste:
|
||||||
|
text = self.sanitize_ibp_pat.sub('', text)
|
||||||
|
|
||||||
for chunk in self.ebs_pat.split(text):
|
for chunk in self.ebs_pat.split(text):
|
||||||
if chunk == '\r':
|
if chunk == '\r':
|
||||||
self.handler.on_key(enter_key)
|
self.handler.on_key(enter_key)
|
||||||
elif chunk == '\177':
|
elif chunk == '\177':
|
||||||
self.handler.on_key(backspace_key)
|
self.handler.on_key(backspace_key)
|
||||||
else:
|
else:
|
||||||
self.handler.on_text(chunk)
|
self.handler.on_text(chunk, self.in_bracketed_paste)
|
||||||
|
|
||||||
def on_dcs(self, dcs):
|
def on_dcs(self, dcs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_csi(self, csi):
|
def on_csi(self, csi):
|
||||||
pass
|
q = csi[-1]
|
||||||
|
if q in 'mM':
|
||||||
|
pass
|
||||||
|
elif q == '~':
|
||||||
|
if csi == '200~':
|
||||||
|
self.in_bracketed_paste = True
|
||||||
|
elif csi == '201~':
|
||||||
|
self.in_bracketed_paste = False
|
||||||
|
|
||||||
def on_pm(self, pm):
|
def on_pm(self, pm):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -12,14 +12,24 @@ parse_input_from_terminal(PyObject *self UNUSED, PyObject *args) {
|
|||||||
enum State { NORMAL, ESC, CSI, ST, ESC_ST };
|
enum State { NORMAL, ESC, CSI, ST, ESC_ST };
|
||||||
enum State state = NORMAL;
|
enum State state = NORMAL;
|
||||||
PyObject *uo, *text_callback, *dcs_callback, *csi_callback, *osc_callback, *pm_callback, *apc_callback, *callback;
|
PyObject *uo, *text_callback, *dcs_callback, *csi_callback, *osc_callback, *pm_callback, *apc_callback, *callback;
|
||||||
if (!PyArg_ParseTuple(args, "OOOOOOU", &text_callback, &dcs_callback, &csi_callback, &osc_callback, &pm_callback, &apc_callback, &uo)) return NULL;
|
int inbp = 0;
|
||||||
|
if (!PyArg_ParseTuple(args, "OOOOOOUp", &text_callback, &dcs_callback, &csi_callback, &osc_callback, &pm_callback, &apc_callback, &uo, &inbp)) return NULL;
|
||||||
Py_ssize_t sz = PyUnicode_GET_LENGTH(uo), pos = 0, start = 0, count = 0, consumed = 0;
|
Py_ssize_t sz = PyUnicode_GET_LENGTH(uo), pos = 0, start = 0, count = 0, consumed = 0;
|
||||||
callback = text_callback;
|
callback = text_callback;
|
||||||
int kind = PyUnicode_KIND(uo);
|
int kind = PyUnicode_KIND(uo);
|
||||||
void *data = PyUnicode_DATA(uo);
|
void *data = PyUnicode_DATA(uo);
|
||||||
#define CALL(cb, s, num) {\
|
bool in_bracketed_paste_mode = inbp != 0;
|
||||||
if (num > 0) PyObject_CallFunction(cb, "N", PyUnicode_Substring(uo, s, s + num)); \
|
#define CALL(cb, s_, num_) {\
|
||||||
consumed = s + num; \
|
PyObject *fcb = cb; \
|
||||||
|
Py_ssize_t s = s_, num = num_; \
|
||||||
|
if (in_bracketed_paste_mode && fcb != text_callback) { \
|
||||||
|
fcb = text_callback; num += 2; s -= 2; \
|
||||||
|
} \
|
||||||
|
if (num > 0) { \
|
||||||
|
PyObject *ret = PyObject_CallFunction(fcb, "N", PyUnicode_Substring(uo, s, s + num)); \
|
||||||
|
Py_XDECREF(ret); \
|
||||||
|
} \
|
||||||
|
consumed = s_ + num_; \
|
||||||
count = 0; \
|
count = 0; \
|
||||||
}
|
}
|
||||||
START_ALLOW_CASE_RANGE;
|
START_ALLOW_CASE_RANGE;
|
||||||
@ -30,6 +40,7 @@ parse_input_from_terminal(PyObject *self UNUSED, PyObject *args) {
|
|||||||
if (ch == 0x1b) {
|
if (ch == 0x1b) {
|
||||||
state = ESC;
|
state = ESC;
|
||||||
CALL(text_callback, start, count);
|
CALL(text_callback, start, count);
|
||||||
|
start = pos;
|
||||||
} else count++;
|
} else count++;
|
||||||
break;
|
break;
|
||||||
case ESC:
|
case ESC:
|
||||||
@ -55,7 +66,17 @@ parse_input_from_terminal(PyObject *self UNUSED, PyObject *args) {
|
|||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'a' ... 'z':
|
case 'a' ... 'z':
|
||||||
case 'A' ... 'Z':
|
case 'A' ... 'Z':
|
||||||
|
case '@':
|
||||||
|
case '`':
|
||||||
|
case '{':
|
||||||
|
case '|':
|
||||||
|
case '}':
|
||||||
|
case '~':
|
||||||
|
#define IBP(w) ch == '~' && PyUnicode_READ(kind, data, start + 1) == '2' && PyUnicode_READ(kind, data, start + 2) == '0' && PyUnicode_READ(kind, data, start + 3) == w
|
||||||
|
if (IBP('1')) in_bracketed_paste_mode = false;
|
||||||
CALL(callback, start + 1, count);
|
CALL(callback, start + 1, count);
|
||||||
|
if (IBP('0')) in_bracketed_paste_mode = true;
|
||||||
|
#undef IBP
|
||||||
state = NORMAL;
|
state = NORMAL;
|
||||||
start = pos + 1;
|
start = pos + 1;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -339,17 +339,28 @@ class TestDataTypes(BaseTest):
|
|||||||
self.ae(wcswidth('\u2716\u2716\ufe0f\U0001f337'), 5)
|
self.ae(wcswidth('\u2716\u2716\ufe0f\U0001f337'), 5)
|
||||||
self.ae(sanitize_title('a\0\01 \t\n\f\rb'), 'a b')
|
self.ae(sanitize_title('a\0\01 \t\n\f\rb'), 'a b')
|
||||||
|
|
||||||
def tp(*data, leftover='', text='', csi='', apc=''):
|
def tp(*data, leftover='', text='', csi='', apc='', ibp=False):
|
||||||
text_r, csi_r, apc_r, rest = [], [], [], []
|
text_r, csi_r, apc_r, rest = [], [], [], []
|
||||||
left = ''
|
left = ''
|
||||||
|
in_bp = ibp
|
||||||
|
|
||||||
|
def on_csi(x):
|
||||||
|
nonlocal in_bp
|
||||||
|
if x == '200~':
|
||||||
|
in_bp = True
|
||||||
|
elif x == '201~':
|
||||||
|
in_bp = False
|
||||||
|
csi_r.append(x)
|
||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
left = parse_input_from_terminal(text_r.append, rest.append, csi_r.append, rest.append, rest.append, apc_r.append, left + d)
|
left = parse_input_from_terminal(text_r.append, rest.append, on_csi, rest.append, rest.append, apc_r.append, left + d, in_bp)
|
||||||
self.ae(left, leftover)
|
self.ae(left, leftover)
|
||||||
self.ae(text, ' '.join(text_r))
|
self.ae(text, ' '.join(text_r))
|
||||||
self.ae(csi, ' '.join(csi_r))
|
self.ae(csi, ' '.join(csi_r))
|
||||||
self.ae(apc, ' '.join(apc_r))
|
self.ae(apc, ' '.join(apc_r))
|
||||||
self.assertFalse(rest)
|
self.assertFalse(rest)
|
||||||
|
|
||||||
|
tp('a\033[200~\033[32mxy\033[201~\033[33ma', text='a \033[32m xy a', csi='200~ 201~ 33m')
|
||||||
tp('abc', text='abc')
|
tp('abc', text='abc')
|
||||||
tp('a\033[38:5:12:32mb', text='a b', csi='38:5:12:32m')
|
tp('a\033[38:5:12:32mb', text='a b', csi='38:5:12:32m')
|
||||||
tp('a\033_x,;(\033\\b', text='a b', apc='x,;(')
|
tp('a\033_x,;(\033\\b', text='a b', apc='x,;(')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user