Add tests for hyperlink storage
This commit is contained in:
parent
33beecddda
commit
78dc93721d
@ -10,6 +10,8 @@
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_KEY_LEN 2048
|
||||
#define MAX_ID_LEN 256
|
||||
#define MAX_ADDS_BEFORE_GC 256
|
||||
#undef uthash_fatal
|
||||
#define uthash_fatal(msg) fatal(msg)
|
||||
|
||||
@ -69,8 +71,8 @@ free_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
garbage_collect_pool(Screen *screen) {
|
||||
void
|
||||
screen_garbage_collect_hyperlink_pool(Screen *screen) {
|
||||
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
|
||||
pool->num_of_adds_since_garbage_collection = 0;
|
||||
if (!pool->max_link_id) return;
|
||||
@ -99,14 +101,14 @@ get_id_for_hyperlink(Screen *screen, const char *id, const char *url) {
|
||||
if (!url) return 0;
|
||||
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
|
||||
static char key[MAX_KEY_LEN] = {0};
|
||||
size_t keylen = snprintf(key, MAX_KEY_LEN-1, "%s:%s", id ? id : "", url);
|
||||
size_t keylen = snprintf(key, MAX_KEY_LEN-1, "%.*s:%s", MAX_ID_LEN, id ? id : "", url);
|
||||
HyperLinkEntry *s = NULL;
|
||||
if (pool->hyperlinks) {
|
||||
HASH_FIND_STR(pool->hyperlinks, key, s);
|
||||
if (s) return s->id;
|
||||
}
|
||||
hyperlink_id_type new_id = 0;
|
||||
if (pool->num_of_adds_since_garbage_collection >= 256) garbage_collect_pool(screen);
|
||||
if (pool->num_of_adds_since_garbage_collection >= MAX_ADDS_BEFORE_GC) screen_garbage_collect_hyperlink_pool(screen);
|
||||
if (pool->max_link_id >= HYPERLINK_MAX_NUMBER) {
|
||||
log_error("Too many hyperlinks, discarding oldest, this means some hyperlinks might be incorrect");
|
||||
new_id = pool->hyperlinks->id;
|
||||
@ -124,3 +126,27 @@ get_id_for_hyperlink(Screen *screen, const char *id, const char *url) {
|
||||
pool->num_of_adds_since_garbage_collection++;
|
||||
return s->id;
|
||||
}
|
||||
|
||||
const char*
|
||||
get_hyperlink_for_id(Screen *screen, hyperlink_id_type id) {
|
||||
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
|
||||
HyperLinkEntry *s, *tmp;
|
||||
HASH_ITER(hh, pool->hyperlinks, s, tmp) {
|
||||
if (s->id == id) return strstr(s->key, ":") + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
screen_hyperlinks_as_list(Screen *screen) {
|
||||
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
|
||||
PyObject *ans = PyList_New(0);
|
||||
HyperLinkEntry *s, *tmp;
|
||||
HASH_ITER(hh, pool->hyperlinks, s, tmp) {
|
||||
PyObject *e = Py_BuildValue("sH", s->key, s->id);
|
||||
PyList_Append(ans, e);
|
||||
Py_DECREF(e);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
@ -13,3 +13,6 @@ void free_hyperlink_pool(HYPERLINK_POOL_HANDLE);
|
||||
void clear_hyperlink_pool(HYPERLINK_POOL_HANDLE);
|
||||
hyperlink_id_type get_id_for_hyperlink(Screen*, const char*, const char*);
|
||||
hyperlink_id_type remap_hyperlink_ids(Screen *self, hyperlink_id_type *map);
|
||||
PyObject* screen_hyperlinks_as_list(Screen *screen);
|
||||
void screen_garbage_collect_hyperlink_pool(Screen *screen);
|
||||
const char* get_hyperlink_for_id(Screen *screen, hyperlink_id_type id);
|
||||
|
||||
13
kitty/line.c
13
kitty/line.c
@ -817,12 +817,22 @@ end:
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
// Boilerplate {{{
|
||||
static PyObject*
|
||||
copy_char(Line* self, PyObject *args);
|
||||
#define copy_char_doc "copy_char(src, to, dest) -> Copy the character at src to to the character dest in the line `to`"
|
||||
|
||||
#define hyperlink_ids_doc "hyperlink_ids() -> Tuple of hyper link ids at every cell"
|
||||
static PyObject*
|
||||
hyperlink_ids(Line *self, PyObject *args UNUSED) {
|
||||
PyObject *ans = PyTuple_New(self->xnum);
|
||||
for (index_type x = 0; x < self->xnum; x++) {
|
||||
PyTuple_SET_ITEM(ans, x, PyLong_FromUnsignedLong(self->cpu_cells[x].hyperlink_id));
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
richcmp(PyObject *obj1, PyObject *obj2, int op);
|
||||
|
||||
@ -845,6 +855,7 @@ static PyMethodDef methods[] = {
|
||||
METHOD(set_attribute, METH_VARARGS)
|
||||
METHOD(as_ansi, METH_NOARGS)
|
||||
METHOD(is_continued, METH_NOARGS)
|
||||
METHOD(hyperlink_ids, METH_NOARGS)
|
||||
METHOD(width, METH_O)
|
||||
METHOD(url_start_at, METH_O)
|
||||
METHOD(url_end_at, METH_VARARGS)
|
||||
|
||||
@ -1911,6 +1911,20 @@ deactivate_overlay_line(Screen *self) {
|
||||
#define WRAP2(name, defval1, defval2) static PyObject* name(Screen *self, PyObject *args) { unsigned int a=defval1, b=defval2; if(!PyArg_ParseTuple(args, "|II", &a, &b)) return NULL; screen_##name(self, a, b); Py_RETURN_NONE; }
|
||||
#define WRAP2B(name) static PyObject* name(Screen *self, PyObject *args) { unsigned int a, b; int p; if(!PyArg_ParseTuple(args, "IIp", &a, &b, &p)) return NULL; screen_##name(self, a, b, (bool)p); Py_RETURN_NONE; }
|
||||
|
||||
WRAP0(garbage_collect_hyperlink_pool)
|
||||
|
||||
static PyObject*
|
||||
hyperlinks_as_list(Screen *self, PyObject *args UNUSED) {
|
||||
return screen_hyperlinks_as_list(self);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
hyperlink_for_id(Screen *self, PyObject *val) {
|
||||
unsigned long id = PyLong_AsUnsignedLong(val);
|
||||
if (id > HYPERLINK_MAX_NUMBER) { PyErr_SetString(PyExc_IndexError, "Out of bounds"); return NULL; }
|
||||
return Py_BuildValue("s", get_hyperlink_for_id(self, id));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_pending_timeout(Screen *self, PyObject *val) {
|
||||
if (!PyFloat_Check(val)) { PyErr_SetString(PyExc_TypeError, "timeout must be a float"); return NULL; }
|
||||
@ -2638,6 +2652,9 @@ static PyMethodDef methods[] = {
|
||||
MND(erase_in_line, METH_VARARGS)
|
||||
MND(erase_in_display, METH_VARARGS)
|
||||
MND(scroll_until_cursor, METH_NOARGS)
|
||||
MND(hyperlinks_as_list, METH_NOARGS)
|
||||
MND(garbage_collect_hyperlink_pool, METH_NOARGS)
|
||||
MND(hyperlink_for_id, METH_O)
|
||||
METHOD(current_char_width, METH_NOARGS)
|
||||
MND(insert_lines, METH_VARARGS)
|
||||
MND(delete_lines, METH_VARARGS)
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from kitty.fast_data_types import (
|
||||
DECAWM, DECCOLM, DECOM, IRM, Cursor, parse_bytes
|
||||
)
|
||||
from kitty.marks import marker_from_function, marker_from_regex
|
||||
|
||||
from . import BaseTest
|
||||
from kitty.fast_data_types import DECAWM, IRM, Cursor, DECCOLM, DECOM
|
||||
from kitty.marks import marker_from_regex, marker_from_function
|
||||
|
||||
|
||||
class TestScreen(BaseTest):
|
||||
@ -516,3 +519,69 @@ class TestScreen(BaseTest):
|
||||
self.ae(s.marked_cells(), cells(8))
|
||||
s.set_marker(marker_from_regex('\t', 3))
|
||||
self.ae(s.marked_cells(), cells(*range(8)))
|
||||
|
||||
def test_hyperlinks(self):
|
||||
s = self.create_screen()
|
||||
self.ae(s.line(0).hyperlink_ids(), tuple(0 for x in range(s.columns)))
|
||||
|
||||
def set_link(url=None, id=None):
|
||||
parse_bytes(s, '\x1b]8;id={};{}\x1b\\'.format(id or '', url or '').encode('utf-8'))
|
||||
|
||||
set_link('url-a', 'a')
|
||||
self.ae(s.line(0).hyperlink_ids(), tuple(0 for x in range(s.columns)))
|
||||
s.draw('a')
|
||||
self.ae(s.line(0).hyperlink_ids(), (1,) + tuple(0 for x in range(s.columns - 1)))
|
||||
s.draw('bc')
|
||||
self.ae(s.line(0).hyperlink_ids(), (1, 1, 1, 0, 0))
|
||||
set_link()
|
||||
s.draw('d')
|
||||
self.ae(s.line(0).hyperlink_ids(), (1, 1, 1, 0, 0))
|
||||
set_link('url-a', 'a')
|
||||
s.draw('efg')
|
||||
self.ae(s.line(0).hyperlink_ids(), (1, 1, 1, 0, 1))
|
||||
self.ae(s.line(1).hyperlink_ids(), (1, 1, 0, 0, 0))
|
||||
set_link('url-b')
|
||||
s.draw('hij')
|
||||
self.ae(s.line(1).hyperlink_ids(), (1, 1, 2, 2, 2))
|
||||
set_link()
|
||||
self.ae([('a:url-a', 1), (':url-b', 2)], s.hyperlinks_as_list())
|
||||
s.garbage_collect_hyperlink_pool()
|
||||
self.ae([('a:url-a', 1), (':url-b', 2)], s.hyperlinks_as_list())
|
||||
for i in range(s.lines + 2):
|
||||
s.linefeed()
|
||||
s.garbage_collect_hyperlink_pool()
|
||||
self.ae([('a:url-a', 1), (':url-b', 2)], s.hyperlinks_as_list())
|
||||
for i in range(s.lines * 2):
|
||||
s.linefeed()
|
||||
s.garbage_collect_hyperlink_pool()
|
||||
self.assertFalse(s.hyperlinks_as_list())
|
||||
set_link('url-a', 'x')
|
||||
s.draw('a')
|
||||
set_link('url-a', 'y')
|
||||
s.draw('a')
|
||||
set_link()
|
||||
self.ae([('x:url-a', 1), ('y:url-a', 2)], s.hyperlinks_as_list())
|
||||
|
||||
s = self.create_screen()
|
||||
set_link('u' * 2048)
|
||||
s.draw('a')
|
||||
self.ae([(':' + 'u' * 2045, 1)], s.hyperlinks_as_list())
|
||||
s = self.create_screen()
|
||||
set_link('u' * 2048, 'i' * 300)
|
||||
s.draw('a')
|
||||
self.ae([('i'*256 + ':' + 'u' * (2045 - 256), 1)], s.hyperlinks_as_list())
|
||||
|
||||
s = self.create_screen()
|
||||
set_link('1'), s.draw('1')
|
||||
set_link('2'), s.draw('2')
|
||||
set_link('3'), s.draw('3')
|
||||
s.cursor.x = 1
|
||||
set_link(), s.draw('X')
|
||||
self.ae(s.line(0).hyperlink_ids(), (1, 0, 3, 0, 0))
|
||||
self.ae([(':1', 1), (':2', 2), (':3', 3)], s.hyperlinks_as_list())
|
||||
s.garbage_collect_hyperlink_pool()
|
||||
self.ae([(':1', 1), (':3', 2)], s.hyperlinks_as_list())
|
||||
set_link('3'), s.draw('3')
|
||||
self.ae([(':1', 1), (':3', 2)], s.hyperlinks_as_list())
|
||||
set_link('4'), s.draw('4')
|
||||
self.ae([(':1', 1), (':3', 2), (':4', 3)], s.hyperlinks_as_list())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user