Implement storage of URLs in a hash map
This commit is contained in:
parent
e99d93ca30
commit
33beecddda
@ -41,7 +41,8 @@ void log_error(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
typedef unsigned long long id_type;
|
||||
typedef uint32_t char_type;
|
||||
typedef uint32_t color_type;
|
||||
typedef uint32_t hyperlink_id_type;
|
||||
typedef uint16_t hyperlink_id_type;
|
||||
#define HYPERLINK_MAX_NUMBER UINT16_MAX
|
||||
typedef uint16_t combining_type;
|
||||
typedef uint32_t pixel;
|
||||
typedef unsigned int index_type;
|
||||
@ -249,6 +250,7 @@ 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)
|
||||
|
||||
@ -155,6 +155,11 @@ historybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) {
|
||||
init_line(self, index_of(self, lnum), l);
|
||||
}
|
||||
|
||||
CPUCell*
|
||||
historybuf_cpu_cells(HistoryBuf *self, index_type lnum) {
|
||||
return cpu_lineptr(self, index_of(self, lnum));
|
||||
}
|
||||
|
||||
void
|
||||
historybuf_mark_line_clean(HistoryBuf *self, index_type y) {
|
||||
line_attrs_type *p = attrptr(self, index_of(self, y));
|
||||
|
||||
@ -5,24 +5,122 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "hyperlink.h"
|
||||
#include "uthash.h"
|
||||
#include <string.h>
|
||||
#include "screen.h"
|
||||
|
||||
bool
|
||||
parse_osc_8(char *buf, char **id, char **url) {
|
||||
char *boundary = strstr(buf, ";");
|
||||
if (boundary == NULL) return false;
|
||||
*boundary = 0;
|
||||
if (*(boundary + 1)) *url = boundary + 1;
|
||||
char *save, *token = strtok_r(buf, ":", &save);
|
||||
while (token != NULL) {
|
||||
size_t len = strlen(token);
|
||||
if (len > 3 && token[0] == 'i' && token[1] == 'd' && token[2] == '=' && token[3]) {
|
||||
*id = token + 3;
|
||||
break;
|
||||
}
|
||||
token = strtok_r(NULL, ":", &save);
|
||||
}
|
||||
return true;
|
||||
#define MAX_KEY_LEN 2048
|
||||
#undef uthash_fatal
|
||||
#define uthash_fatal(msg) fatal(msg)
|
||||
|
||||
typedef struct {
|
||||
const char *key;
|
||||
hyperlink_id_type id;
|
||||
UT_hash_handle hh;
|
||||
} HyperLinkEntry;
|
||||
|
||||
|
||||
typedef struct {
|
||||
HyperLinkEntry *hyperlinks;
|
||||
unsigned int max_link_id, num_of_adds_since_garbage_collection;
|
||||
} HyperLinkPool;
|
||||
|
||||
|
||||
static void
|
||||
free_hyperlink_entry(HyperLinkEntry *s) {
|
||||
free((void*)s->key);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_pool(HyperLinkPool *pool) {
|
||||
if (pool->hyperlinks) {
|
||||
HyperLinkEntry *tmp, *s;
|
||||
HASH_ITER(hh, pool->hyperlinks, s, tmp) {
|
||||
HASH_DEL(pool->hyperlinks, s);
|
||||
free_hyperlink_entry(s);
|
||||
}
|
||||
pool->max_link_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HYPERLINK_POOL_HANDLE
|
||||
alloc_hyperlink_pool(void) {
|
||||
return calloc(1, sizeof(HyperLinkPool));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
clear_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {
|
||||
if (h) {
|
||||
HyperLinkPool *pool = (HyperLinkPool*)h;
|
||||
clear_pool(pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
free_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {
|
||||
if (h) {
|
||||
HyperLinkPool *pool = (HyperLinkPool*)h;
|
||||
clear_pool(pool);
|
||||
free(pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
garbage_collect_pool(Screen *screen) {
|
||||
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
|
||||
pool->num_of_adds_since_garbage_collection = 0;
|
||||
if (!pool->max_link_id) return;
|
||||
hyperlink_id_type *map = calloc(HYPERLINK_MAX_NUMBER + 4, sizeof(hyperlink_id_type));
|
||||
if (!map) fatal("Out of memory");
|
||||
hyperlink_id_type num = remap_hyperlink_ids(screen, map);
|
||||
if (num) {
|
||||
HyperLinkEntry *s, *tmp;
|
||||
pool->max_link_id = 0;
|
||||
HASH_ITER(hh, pool->hyperlinks, s, tmp) {
|
||||
if (map[s->id]) {
|
||||
s->id = map[s->id];
|
||||
pool->max_link_id = MAX(pool->max_link_id, s->id);
|
||||
} else {
|
||||
HASH_DEL(pool->hyperlinks, s);
|
||||
free_hyperlink_entry(s);
|
||||
}
|
||||
}
|
||||
} else clear_pool(pool);
|
||||
free(map);
|
||||
}
|
||||
|
||||
|
||||
hyperlink_id_type
|
||||
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);
|
||||
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->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;
|
||||
HyperLinkEntry *s = pool->hyperlinks;
|
||||
HASH_DEL(pool->hyperlinks, s);
|
||||
free_hyperlink_entry(s);
|
||||
}
|
||||
s = malloc(sizeof(HyperLinkEntry));
|
||||
if (!s) fatal("Out of memory");
|
||||
s->key = malloc(keylen + 1);
|
||||
if (!s->key) fatal("Out of memory");
|
||||
memcpy((void*)s->key, key, keylen + 1);
|
||||
s->id = new_id ? new_id : ++pool->max_link_id;
|
||||
HASH_ADD_KEYPTR(hh, pool->hyperlinks, s->key, keylen, s);
|
||||
pool->num_of_adds_since_garbage_collection++;
|
||||
return s->id;
|
||||
}
|
||||
|
||||
15
kitty/hyperlink.h
Normal file
15
kitty/hyperlink.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
HYPERLINK_POOL_HANDLE alloc_hyperlink_pool(void);
|
||||
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);
|
||||
16
kitty/line.c
16
kitty/line.c
@ -420,6 +420,7 @@ set_text(Line* self, PyObject *args) {
|
||||
|
||||
for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {
|
||||
self->cpu_cells[i].ch = (PyUnicode_READ(kind, buf, offset));
|
||||
self->cpu_cells[i].hyperlink_id = 0;
|
||||
self->gpu_cells[i].attrs = attrs;
|
||||
self->gpu_cells[i].fg = fg;
|
||||
self->gpu_cells[i].bg = bg;
|
||||
@ -454,15 +455,10 @@ cursor_from(Line* self, PyObject *args) {
|
||||
void
|
||||
line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) {
|
||||
attrs_type width = ch ? 1 : 0;
|
||||
#define PREFIX \
|
||||
for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \
|
||||
self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); \
|
||||
self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \
|
||||
}
|
||||
if (CHAR_IS_BLANK(ch)) {
|
||||
PREFIX
|
||||
} else {
|
||||
PREFIX
|
||||
for (index_type i = at; i < MIN(self->xnum, at + num); i++) {
|
||||
self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
|
||||
self->cpu_cells[i].hyperlink_id = 0;
|
||||
self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width;
|
||||
}
|
||||
}
|
||||
|
||||
@ -486,6 +482,7 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num,
|
||||
for (index_type i = at; i < self->xnum && i < at + num; i++) {
|
||||
if (clear_char) {
|
||||
self->cpu_cells[i].ch = BLANK_CHAR;
|
||||
self->cpu_cells[i].hyperlink_id = 0;
|
||||
memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
|
||||
self->gpu_cells[i].attrs = attrs;
|
||||
clear_sprite_position(self->gpu_cells[i]);
|
||||
@ -517,6 +514,7 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) {
|
||||
char_type w = (self->gpu_cells[self->xnum - 1].attrs) & WIDTH_MASK;
|
||||
if (w != 1) {
|
||||
self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR;
|
||||
self->cpu_cells[self->xnum - 1].hyperlink_id = 0;
|
||||
self->gpu_cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0;
|
||||
clear_sprite_position(self->gpu_cells[self->xnum - 1]);
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ static inline void
|
||||
clear_chars_in_line(CPUCell *cpu_cells, GPUCell *gpu_cells, index_type xnum, char_type ch) {
|
||||
// Clear only the char part of each cell, the rest must have been cleared by a memset or similar
|
||||
if (ch) {
|
||||
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; gpu_cells[i].attrs = 1; }
|
||||
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; cpu_cells[i].hyperlink_id = 0; gpu_cells[i].attrs = 1; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +61,7 @@ left_shift_line(Line *line, index_type at, index_type num) {
|
||||
}
|
||||
if (at < line->xnum && ((line->gpu_cells[at].attrs) & WIDTH_MASK) != 1) {
|
||||
line->cpu_cells[at].ch = BLANK_CHAR;
|
||||
line->cpu_cells[at].hyperlink_id = 0;
|
||||
line->gpu_cells[at].attrs = BLANK_CHAR ? 1 : 0;
|
||||
clear_sprite_position(line->gpu_cells[at]);
|
||||
}
|
||||
@ -102,6 +103,7 @@ void linebuf_refresh_sprite_positions(LineBuf *self);
|
||||
void historybuf_add_line(HistoryBuf *self, const Line *line);
|
||||
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other);
|
||||
void historybuf_init_line(HistoryBuf *self, index_type num, Line *l);
|
||||
CPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num);
|
||||
void historybuf_mark_line_clean(HistoryBuf *self, index_type y);
|
||||
void historybuf_mark_line_dirty(HistoryBuf *self, index_type y);
|
||||
void historybuf_refresh_sprite_positions(HistoryBuf *self);
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
// Need _POSIX_C_SOURCE for strtok_r
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include "data-types.h"
|
||||
#include "control-codes.h"
|
||||
#include "screen.h"
|
||||
@ -306,6 +309,24 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal
|
||||
|
||||
// OSC mode {{{
|
||||
|
||||
static inline bool
|
||||
parse_osc_8(char *buf, char **id, char **url) {
|
||||
char *boundary = strstr(buf, ";");
|
||||
if (boundary == NULL) return false;
|
||||
*boundary = 0;
|
||||
if (*(boundary + 1)) *url = boundary + 1;
|
||||
char *save, *token = strtok_r(buf, ":", &save);
|
||||
while (token != NULL) {
|
||||
size_t len = strlen(token);
|
||||
if (len > 3 && token[0] == 'i' && token[1] == 'd' && token[2] == '=' && token[3]) {
|
||||
*id = token + 3;
|
||||
break;
|
||||
}
|
||||
token = strtok_r(NULL, ":", &save);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
dispatch_hyperlink(Screen *screen, size_t pos, size_t size, PyObject DUMP_UNUSED *dump_callback) {
|
||||
// since the spec says only ASCII printable chars are allowed in OSC 8, we
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
#include "state.h"
|
||||
#include "fonts.h"
|
||||
#include "lineops.h"
|
||||
#include "screen.h"
|
||||
#include "hyperlink.h"
|
||||
#include <structmember.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
@ -129,6 +129,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
init_tabstops(self->main_tabstops, self->columns);
|
||||
init_tabstops(self->alt_tabstops, self->columns);
|
||||
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(); }
|
||||
}
|
||||
return (PyObject*) self;
|
||||
}
|
||||
@ -142,6 +144,7 @@ screen_reset(Screen *self) {
|
||||
if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
||||
linebuf_clear(self->linebuf, BLANK_CHAR);
|
||||
historybuf_clear(self->historybuf);
|
||||
clear_hyperlink_pool(self->hyperlink_pool);
|
||||
grman_clear(self->grman, false, self->cell_size);
|
||||
self->modes = empty_modes;
|
||||
self->active_hyperlink_id = 0;
|
||||
@ -289,6 +292,7 @@ dealloc(Screen* self) {
|
||||
PyMem_Free(self->overlay_line.gpu_cells);
|
||||
PyMem_Free(self->main_tabstops);
|
||||
free(self->pending_mode.buf);
|
||||
free_hyperlink_pool(self->hyperlink_pool);
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
} // }}}
|
||||
|
||||
@ -368,8 +372,33 @@ set_active_hyperlink(Screen *self, char *id, char *url) {
|
||||
self->active_hyperlink_id = 0;
|
||||
return;
|
||||
}
|
||||
self->active_hyperlink_id = get_id_for_hyperlink(self, id, url);
|
||||
}
|
||||
|
||||
hyperlink_id_type
|
||||
remap_hyperlink_ids(Screen *self, hyperlink_id_type *map) {
|
||||
#define PROCESS_CELL(cell) { hid = (cell).hyperlink_id; if (hid) { if (!map[hid]) map[hid] = ++num; (cell).hyperlink_id = map[hid]; }}
|
||||
hyperlink_id_type num = 0, hid;
|
||||
if (self->historybuf->count) {
|
||||
for (index_type y = self->historybuf->count; y-- > 0;) {
|
||||
CPUCell *cells = historybuf_cpu_cells(self->historybuf, y);
|
||||
for (index_type x = 0; x < self->historybuf->xnum; x++) {
|
||||
PROCESS_CELL(cells[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
LineBuf *second = self->linebuf, *first = second == self->main_linebuf ? self->alt_linebuf : self->main_linebuf;
|
||||
for (index_type i = 0; i < self->lines * self->columns; i++) {
|
||||
PROCESS_CELL(first->cpu_cell_buf[i]);
|
||||
}
|
||||
for (index_type i = 0; i < self->lines * self->columns; i++) {
|
||||
PROCESS_CELL(second->cpu_cell_buf[i]);
|
||||
}
|
||||
return num;
|
||||
#undef PROCESS_CELL
|
||||
}
|
||||
|
||||
|
||||
static inline bool is_flag_pair(char_type a, char_type b) {
|
||||
return is_flag_codepoint(a) && is_flag_codepoint(b);
|
||||
}
|
||||
|
||||
@ -124,6 +124,7 @@ typedef struct {
|
||||
bool has_focus;
|
||||
bool has_activity_since_last_focus;
|
||||
hyperlink_id_type active_hyperlink_id;
|
||||
HYPERLINK_POOL_HANDLE hyperlink_pool;
|
||||
} Screen;
|
||||
|
||||
|
||||
@ -207,7 +208,6 @@ bool screen_history_scroll(Screen *self, int amt, bool upwards);
|
||||
Line* screen_visual_line(Screen *self, index_type y);
|
||||
unsigned long screen_current_char_width(Screen *self);
|
||||
void screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y);
|
||||
bool parse_osc_8(char *buf, char **id, char **url);
|
||||
void set_active_hyperlink(Screen*, char*, char*);
|
||||
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
|
||||
bool screen_open_url(Screen*);
|
||||
|
||||
1227
kitty/uthash.h
Normal file
1227
kitty/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user