Output hyperlink markup when serializing as ANSI

This commit is contained in:
Kovid Goyal 2020-09-22 16:18:22 +05:30
parent e0f5c39297
commit 9d4246a285
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 59 additions and 2 deletions

View File

@ -199,9 +199,12 @@ typedef struct {
bool rewrap_needed;
} PagerHistoryBuf;
typedef struct {int x;} *HYPERLINK_POOL_HANDLE;
typedef struct {
Py_UCS4 *buf;
size_t len, capacity;
HYPERLINK_POOL_HANDLE hyperlink_pool;
hyperlink_id_type active_hyperlink_id;
} ANSIBuf;
typedef struct {
@ -254,7 +257,6 @@ typedef struct {
typedef struct {int x;} *SPRITE_MAP_HANDLE;
#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; unsigned int cell_width, cell_height;
typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;
typedef struct {int x;} *HYPERLINK_POOL_HANDLE;
#define PARSER_BUF_SZ (8 * 1024)
#define READ_BUF_SZ (1024*1024)

View File

@ -277,11 +277,36 @@ write_sgr(const char *val, ANSIBuf *output) {
#undef W
}
static inline void
write_hyperlink(hyperlink_id_type hid, ANSIBuf *output) {
#define W(c) output->buf[output->len++] = c
const char *key = hid ? get_hyperlink_for_id(output->hyperlink_pool, hid, false) : NULL;
if (!key) hid = 0;
output->active_hyperlink_id = hid;
W(0x1b); W(']'); W('8');
if (!hid) {
W(';'); W(';');
} else {
const char* partition = strstr(key, ":");
W(';');
if (partition != key) {
W('i'); W('d'); W('=');
while (key != partition) W(*(key++));
}
W(';');
while(*(++partition)) W(*partition);
}
W(0x1b); W('\\');
#undef W
}
void
line_as_ansi(Line *self, ANSIBuf *output, const GPUCell** prev_cell) {
#define ENSURE_SPACE(extra) ensure_space_for(output, buf, Py_UCS4, output->len + extra, capacity, 2048, false);
#define WRITE_SGR(val) { ENSURE_SPACE(128); write_sgr(val, output); }
#define WRITE_CH(val) { ENSURE_SPACE(1); output->buf[output->len++] = val; }
#define WRITE_HYPERLINK(val) { ENSURE_SPACE(2256); write_hyperlink(val, output); }
output->len = 0;
index_type limit = xlimit_for_line(self);
if (limit == 0) return;
@ -297,6 +322,12 @@ line_as_ansi(Line *self, ANSIBuf *output, const GPUCell** prev_cell) {
if (previous_width == 2) { previous_width = 0; continue; }
ch = ' ';
}
if (output->hyperlink_pool) {
hyperlink_id_type hid = self->cpu_cells[pos].hyperlink_id;
if (hid != output->active_hyperlink_id) {
WRITE_HYPERLINK(hid);
}
}
cell = &self->gpu_cells[pos];
@ -325,6 +356,7 @@ line_as_ansi(Line *self, ANSIBuf *output, const GPUCell** prev_cell) {
#undef WRITE_SGR
#undef WRITE_CH
#undef ENSURE_SPACE
#undef WRITE_HYPERLINK
}
static PyObject*
@ -770,8 +802,9 @@ as_text_generic(PyObject *args, void *container, get_line_func get_line, index_t
PyObject *nl = PyUnicode_FromString("\n");
PyObject *cr = PyUnicode_FromString("\r");
PyObject *sgr_reset = PyUnicode_FromString("\x1b[m");
if (nl == NULL || cr == NULL || sgr_reset == NULL) goto end;
const GPUCell *prev_cell = NULL;
if (nl == NULL || cr == NULL) goto end;
ansibuf->active_hyperlink_id = 0;
for (index_type y = 0; y < lines; y++) {
Line *line = get_line(container, y);
if (!line->continued && y > 0) {
@ -805,6 +838,15 @@ as_text_generic(PyObject *args, void *container, get_line_func get_line, index_t
Py_CLEAR(ret);
}
}
if (ansibuf->active_hyperlink_id) {
ansibuf->active_hyperlink_id = 0;
t = PyUnicode_FromString("\x1b]8;;\x1b\\");
if (t) {
ret = PyObject_CallFunctionObjArgs(callback, t, NULL);
Py_CLEAR(t);
Py_CLEAR(ret);
}
}
end:
Py_CLEAR(nl); Py_CLEAR(cr); Py_CLEAR(sgr_reset);
if (PyErr_Occurred()) return NULL;

View File

@ -137,6 +137,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
if (!init_overlay_line(self, self->columns)) { Py_CLEAR(self); return NULL; }
self->hyperlink_pool = alloc_hyperlink_pool();
if (!self->hyperlink_pool) { Py_CLEAR(self); return PyErr_NoMemory(); }
self->as_ansi_buf.hyperlink_pool = self->hyperlink_pool;
}
return (PyObject*) self;
}

View File

@ -467,6 +467,18 @@ class TestScreen(BaseTest):
s.draw(f'{i}' * s.columns)
self.ae(as_text(s, True, True), '\x1b[m\x1b[31m11\x1b[m\x1b[32m22\x1b[m\x1b[33m33\x1b[m\x1b[34m44\x1b[m\x1b[m\x1b[35m55\x1b[m\x1b[36m66')
def set_link(url=None, id=None):
parse_bytes(s, '\x1b]8;id={};{}\x1b\\'.format(id or '', url or '').encode('utf-8'))
s = self.create_screen()
s.draw('a')
set_link('moo', 'foo')
s.draw('bcdef')
self.ae(as_text(s, True), '\x1b[ma\x1b]8;id=foo;moo\x1b\\bcde\x1b[mf\n\n\n\x1b]8;;\x1b\\')
set_link()
s.draw('gh')
self.ae(as_text(s, True), '\x1b[ma\x1b]8;id=foo;moo\x1b\\bcde\x1b[mf\x1b]8;;\x1b\\gh\n\n\n')
def test_pagerhist(self):
hsz = 8
s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': hsz})