Initial port of code to use TextCache
This commit is contained in:
parent
f6347ee1e2
commit
cabd6c0589
14 changed files with 457 additions and 344 deletions
|
|
@ -59,6 +59,7 @@ static inline PyObject* Py_XNewRef(PyObject *o) { Py_XINCREF(o); return o; }
|
|||
|
||||
typedef unsigned long long id_type;
|
||||
typedef uint32_t char_type;
|
||||
#define MAX_CHAR_TYPE_VALUE UINT32_MAX
|
||||
typedef uint32_t color_type;
|
||||
typedef uint16_t hyperlink_id_type;
|
||||
typedef int key_type;
|
||||
|
|
@ -119,7 +120,6 @@ typedef struct ImageAnchorPosition {
|
|||
#define BLANK_CHAR 0
|
||||
#define COL_MASK 0xFFFFFFFF
|
||||
#define DECORATION_FG_CODE 58
|
||||
#define CHAR_IS_BLANK(ch) ((ch) == 32 || (ch) == 0)
|
||||
|
||||
// PUA character used as an image placeholder.
|
||||
#define IMAGE_PLACEHOLDER_CHAR 0x10EEEE
|
||||
|
|
@ -197,14 +197,6 @@ typedef struct {
|
|||
uint32_t left, top, right, bottom;
|
||||
} Region;
|
||||
|
||||
typedef union CharOrIndex {
|
||||
struct {
|
||||
char_type ch_is_index: 1;
|
||||
char_type ch: sizeof(char_type) - 1;
|
||||
};
|
||||
char_type val;
|
||||
} CharOrIndex;
|
||||
|
||||
typedef enum { UNKNOWN_PROMPT_KIND = 0, PROMPT_START = 1, SECONDARY_PROMPT = 2, OUTPUT_START = 3 } PromptKind;
|
||||
typedef struct {int x;} *HYPERLINK_POOL_HANDLE;
|
||||
typedef struct {
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ end:
|
|||
static bool face_has_codepoint(const void *face, char_type cp) { return glyph_id_for_codepoint(face, cp) > 0; }
|
||||
|
||||
PyObject*
|
||||
create_fallback_face(PyObject UNUSED *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
|
||||
create_fallback_face(PyObject UNUSED *base_face, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
|
||||
ensure_initialized();
|
||||
PyObject *ans = NULL;
|
||||
RAII_PyObject(d, NULL);
|
||||
|
|
@ -505,7 +505,7 @@ create_fallback_face(PyObject UNUSED *base_face, CPUCell* cell, bool bold, bool
|
|||
if (!emoji_presentation && bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
||||
if (!emoji_presentation && italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
||||
if (emoji_presentation) { AP(FcPatternAddBool, FC_COLOR, true, "color"); }
|
||||
size_t num = cell_as_unicode_for_fallback(cell, char_buf);
|
||||
size_t num = cell_as_unicode_for_fallback(lc, char_buf);
|
||||
add_charset(pat, num);
|
||||
d = _fc_match(pat);
|
||||
face_from_descriptor:
|
||||
|
|
@ -515,22 +515,22 @@ face_from_descriptor:
|
|||
while ((q = iter_fallback_faces(fg, &idx))) {
|
||||
if (face_equals_descriptor(q, d)) {
|
||||
ans = PyLong_FromSsize_t(idx);
|
||||
if (!glyph_found) glyph_found = has_cell_text(face_has_codepoint, q, cell, false);
|
||||
if (!glyph_found) glyph_found = has_cell_text(face_has_codepoint, q, false, lc);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
ans = face_from_descriptor(d, fg);
|
||||
if (!glyph_found && ans) glyph_found = has_cell_text(face_has_codepoint, ans, cell, false);
|
||||
if (!glyph_found && ans) glyph_found = has_cell_text(face_has_codepoint, ans, false, lc);
|
||||
}
|
||||
end:
|
||||
Py_CLEAR(d);
|
||||
if (pat != NULL) { FcPatternDestroy(pat); pat = NULL; }
|
||||
if (!glyph_found && !PyErr_Occurred()) {
|
||||
if (builtin_nerd_font.face && has_cell_text(face_has_codepoint, builtin_nerd_font.face, cell, false)) {
|
||||
if (builtin_nerd_font.face && has_cell_text(face_has_codepoint, builtin_nerd_font.face, false, lc)) {
|
||||
Py_CLEAR(ans);
|
||||
d = builtin_nerd_font.descriptor; Py_INCREF(d); glyph_found = true; goto face_from_descriptor;
|
||||
} else {
|
||||
if (global_state.debug_font_fallback && ans) has_cell_text(face_has_codepoint, ans, cell, true);
|
||||
if (global_state.debug_font_fallback && ans) has_cell_text(face_has_codepoint, ans, true, lc);
|
||||
Py_CLEAR(ans); ans = Py_None; Py_INCREF(ans);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
228
kitty/fonts.c
228
kitty/fonts.c
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "fonts.h"
|
||||
#include "pyport.h"
|
||||
#include "charsets.h"
|
||||
#include "state.h"
|
||||
#include "emoji.h"
|
||||
#include "unicode-data.h"
|
||||
|
|
@ -465,36 +466,38 @@ face_has_codepoint(const void* face, char_type cp) {
|
|||
}
|
||||
|
||||
static bool
|
||||
has_emoji_presentation(CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
return gpu_cell->attrs.width == 2 && is_emoji(cpu_cell->ch) && cpu_cell->cc_idx[0] != VS15;
|
||||
has_emoji_presentation(const GPUCell *gpu_cell, const ListOfChars *lc) {
|
||||
if (gpu_cell->attrs.width != 2) return false;
|
||||
return lc->count > 1 && is_emoji(lc->chars[0]) && lc->chars[1] != VS15;
|
||||
}
|
||||
|
||||
bool
|
||||
has_cell_text(bool(*has_codepoint)(const void*, char_type ch), const void* face, const CPUCell *cell, bool do_debug) {
|
||||
if (!has_codepoint(face, cell->ch)) goto not_found;
|
||||
char_type combining_chars[arraysz(cell->cc_idx)];
|
||||
has_cell_text(bool(*has_codepoint)(const void*, char_type ch), const void* face, bool do_debug, const ListOfChars *lc) {
|
||||
if (!has_codepoint(face, lc->chars[0])) goto not_found;
|
||||
unsigned num_cc = 0;
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
const char_type ccp = codepoint_for_mark(cell->cc_idx[i]);
|
||||
if (!is_non_rendered_char(ccp)) combining_chars[num_cc++] = ccp;
|
||||
for (unsigned i = 1; i < lc->count; i++) {
|
||||
if (!is_non_rendered_char(lc->chars[i])) lc->chars[i] = 0;
|
||||
else num_cc++;
|
||||
}
|
||||
if (num_cc == 0) return true;
|
||||
if (num_cc == 1) {
|
||||
if (has_codepoint(face, combining_chars[0])) return true;
|
||||
char_type cc = 0;
|
||||
for (unsigned i = 1; i < lc->count && cc; i++) cc = lc->chars[i];
|
||||
if (has_codepoint(face, cc)) return true;
|
||||
char_type ch = 0;
|
||||
if (hb_unicode_compose(hb_unicode_funcs_get_default(), cell->ch, combining_chars[0], &ch) && face_has_codepoint(face, ch)) return true;
|
||||
if (hb_unicode_compose(hb_unicode_funcs_get_default(), lc->chars[0], cc, &ch) && face_has_codepoint(face, ch)) return true;
|
||||
goto not_found;
|
||||
}
|
||||
for (unsigned i = 0; i < num_cc; i++) {
|
||||
if (!has_codepoint(face, combining_chars[i])) goto not_found;
|
||||
for (unsigned i = 1; i < lc->count; i++) {
|
||||
if (lc->chars[i] && !has_codepoint(face, lc->chars[i])) goto not_found;
|
||||
}
|
||||
return true;
|
||||
not_found:
|
||||
if (do_debug) {
|
||||
debug("The font chosen by the OS for the text: ");
|
||||
debug("U+%x ", cell->ch);
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
debug("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
|
||||
debug("U+%x ", lc->chars[0]);
|
||||
for (unsigned i = 1; i < lc->count; i++) {
|
||||
if (lc->chars[i]) debug("U+%x ", lc->chars[i]);
|
||||
}
|
||||
debug("is "); PyObject_Print((PyObject*)face, stderr, 0);
|
||||
debug(" but it does not actually contain glyphs for that text\n");
|
||||
|
|
@ -503,11 +506,9 @@ not_found:
|
|||
}
|
||||
|
||||
static void
|
||||
output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face) {
|
||||
debug("U+%x ", cell->ch);
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
debug("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
|
||||
}
|
||||
output_cell_fallback_data(const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, PyObject *face) {
|
||||
debug("U+%x ", lc->chars[0]);
|
||||
for (unsigned i = 1; i < lc->count; i++) debug("U+%x ", lc->chars[i]);
|
||||
if (bold) debug("bold ");
|
||||
if (italic) debug("italic ");
|
||||
if (emoji_presentation) debug("emoji_presentation ");
|
||||
|
|
@ -527,7 +528,7 @@ iter_fallback_faces(FONTS_DATA_HANDLE fgh, ssize_t *idx) {
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool emoji_presentation) {
|
||||
load_fallback_font(FontGroup *fg, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation) {
|
||||
if (fg->fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
|
||||
ssize_t f;
|
||||
|
||||
|
|
@ -535,10 +536,10 @@ load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool em
|
|||
else f = italic ? fg->italic_font_idx : fg->medium_font_idx;
|
||||
if (f < 0) f = fg->medium_font_idx;
|
||||
|
||||
PyObject *face = create_fallback_face(fg->fonts[f].face, cell, bold, italic, emoji_presentation, (FONTS_DATA_HANDLE)fg);
|
||||
PyObject *face = create_fallback_face(fg->fonts[f].face, lc, bold, italic, emoji_presentation, (FONTS_DATA_HANDLE)fg);
|
||||
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
||||
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
||||
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face);
|
||||
if (global_state.debug_font_fallback) output_cell_fallback_data(lc, bold, italic, emoji_presentation, face);
|
||||
if (PyLong_Check(face)) { ssize_t ans = fg->first_fallback_font_idx + PyLong_AsSsize_t(face); Py_DECREF(face); return ans; }
|
||||
set_size_for_face(face, fg->cell_height, true, (FONTS_DATA_HANDLE)fg);
|
||||
|
||||
|
|
@ -552,18 +553,30 @@ load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool em
|
|||
return ans;
|
||||
}
|
||||
|
||||
size_t
|
||||
chars_as_utf8(const ListOfChars *lc, char *buf, char_type zero_char) {
|
||||
size_t n;
|
||||
if (lc->count == 1) n = encode_utf8(lc->chars[0] ? lc->chars[0] : zero_char, buf);
|
||||
else {
|
||||
n = encode_utf8(lc->chars[0], buf);
|
||||
if (lc->chars[0] != '\t') for (unsigned i = 1; i < lc->count; i++) n += encode_utf8(lc->chars[i], buf + n);
|
||||
}
|
||||
buf[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
fallback_font(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
fallback_font(FontGroup *fg, const GPUCell *gpu_cell, const ListOfChars *lc) {
|
||||
bool bold = gpu_cell->attrs.bold;
|
||||
bool italic = gpu_cell->attrs.italic;
|
||||
bool emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
|
||||
bool emoji_presentation = has_emoji_presentation(gpu_cell, lc);
|
||||
char style = emoji_presentation ? 'a' : 'A';
|
||||
if (bold) style += italic ? 3 : 2; else style += italic ? 1 : 0;
|
||||
char cell_text[8 + arraysz(cpu_cell->cc_idx) * 4] = {style};
|
||||
const size_t cell_text_len = 1 + cell_as_utf8(cpu_cell, true, cell_text + 1, ' ');
|
||||
char cell_text[4 * 32] = {style};
|
||||
const size_t cell_text_len = 1 + chars_as_utf8(lc, cell_text + 1, ' ');
|
||||
fallback_font_map_t_itr fi = vt_get(&fg->fallback_font_map, cell_text);
|
||||
if (!vt_is_end(fi)) return fi.data->val;
|
||||
ssize_t idx = load_fallback_font(fg, cpu_cell, bold, italic, emoji_presentation);
|
||||
ssize_t idx = load_fallback_font(fg, lc, bold, italic, emoji_presentation);
|
||||
const char *alloced_key = strndup(cell_text, cell_text_len);
|
||||
if (alloced_key) vt_insert(&fg->fallback_font_map, alloced_key, idx);
|
||||
return idx;
|
||||
|
|
@ -587,12 +600,13 @@ in_symbol_maps(FontGroup *fg, char_type ch) {
|
|||
// - BOX_FONT
|
||||
// - an index in the fonts list
|
||||
static ssize_t
|
||||
font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell, bool *is_main_font, bool *is_emoji_presentation) {
|
||||
font_for_cell(FontGroup *fg, const CPUCell *cpu_cell, const GPUCell *gpu_cell, bool *is_main_font, bool *is_emoji_presentation, TextCache *tc, ListOfChars *lc) {
|
||||
*is_main_font = false;
|
||||
*is_emoji_presentation = false;
|
||||
text_in_cell(cpu_cell, tc, lc);
|
||||
START_ALLOW_CASE_RANGE
|
||||
ssize_t ans;
|
||||
switch(cpu_cell->ch) {
|
||||
switch(lc->chars[0]) {
|
||||
case 0:
|
||||
case ' ':
|
||||
case 0x2002: // en-space
|
||||
|
|
@ -611,8 +625,8 @@ START_ALLOW_CASE_RANGE
|
|||
case 0xf5d0 ... 0xf60d: // branch drawing characters
|
||||
return BOX_FONT;
|
||||
default:
|
||||
*is_emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
|
||||
ans = in_symbol_maps(fg, cpu_cell->ch);
|
||||
*is_emoji_presentation = has_emoji_presentation(gpu_cell, lc);
|
||||
ans = in_symbol_maps(fg, lc->chars[0]);
|
||||
if (ans > -1) return ans;
|
||||
switch(gpu_cell->attrs.bold | (gpu_cell->attrs.italic << 1)) {
|
||||
case 0:
|
||||
|
|
@ -625,8 +639,8 @@ START_ALLOW_CASE_RANGE
|
|||
ans = fg->bi_font_idx; break;
|
||||
}
|
||||
if (ans < 0) ans = fg->medium_font_idx;
|
||||
if (!*is_emoji_presentation && has_cell_text((bool(*)(const void*, char_type))face_has_codepoint, (fg->fonts + ans)->face, cpu_cell, false)) { *is_main_font = true; return ans; }
|
||||
return fallback_font(fg, cpu_cell, gpu_cell);
|
||||
if (!*is_emoji_presentation && has_cell_text((bool(*)(const void*, char_type))face_has_codepoint, (fg->fonts + ans)->face, false, lc)) { *is_main_font = true; return ans; }
|
||||
return fallback_font(fg, gpu_cell, lc);
|
||||
}
|
||||
END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
|
@ -674,9 +688,10 @@ render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Regi
|
|||
}
|
||||
|
||||
static void
|
||||
render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell, const TextCache *tc) {
|
||||
int error = 0;
|
||||
glyph_index glyph = box_glyph_id(cpu_cell->ch);
|
||||
char_type ch = cell_first_char(cpu_cell, tc);
|
||||
glyph_index glyph = box_glyph_id(ch);
|
||||
SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], &glyph, 1, 0, 1, &error);
|
||||
if (sp == NULL) {
|
||||
sprite_map_set_error(error); PyErr_Print();
|
||||
|
|
@ -687,7 +702,7 @@ render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
|||
if (sp->rendered) return;
|
||||
sp->rendered = true;
|
||||
sp->colored = false;
|
||||
PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cpu_cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0);
|
||||
PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", (unsigned int)ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0);
|
||||
if (ret == NULL) { PyErr_Print(); return; }
|
||||
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));
|
||||
ensure_canvas_can_fit(fg, 1);
|
||||
|
|
@ -698,18 +713,19 @@ render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
|||
}
|
||||
|
||||
static void
|
||||
load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells) {
|
||||
load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, const TextCache *tc) {
|
||||
index_type num;
|
||||
hb_buffer_clear_contents(harfbuzz_buffer);
|
||||
RAII_ListOfChars(lc);
|
||||
while (num_cells) {
|
||||
uint16_t prev_width = 0;
|
||||
for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cpu_cell->cc_idx); first_cpu_cell++, first_gpu_cell++, num_cells--) {
|
||||
for (num = 0; num_cells; first_cpu_cell++, first_gpu_cell++, num_cells--) {
|
||||
if (prev_width == 2) { prev_width = 0; continue; }
|
||||
shape_buffer[num++] = first_cpu_cell->ch;
|
||||
text_in_cell(first_cpu_cell, tc, &lc);
|
||||
if (lc.count + num > arraysz(shape_buffer)) break;
|
||||
memcpy(shape_buffer + num, lc.chars, lc.count * sizeof(shape_buffer[0]));
|
||||
num += lc.count;
|
||||
prev_width = first_gpu_cell->attrs.width;
|
||||
for (unsigned i = 0; i < arraysz(first_cpu_cell->cc_idx) && first_cpu_cell->cc_idx[i]; i++) {
|
||||
shape_buffer[num++] = codepoint_for_mark(first_cpu_cell->cc_idx[i]);
|
||||
}
|
||||
}
|
||||
hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num);
|
||||
}
|
||||
|
|
@ -740,7 +756,7 @@ typedef struct GlyphRenderScratch {
|
|||
static GlyphRenderScratch global_glyph_render_scratch = {0};
|
||||
|
||||
static void
|
||||
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index *glyphs, unsigned glyph_count, bool center_glyph) {
|
||||
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index *glyphs, unsigned glyph_count, bool center_glyph, const TextCache *tc) {
|
||||
#define sp global_glyph_render_scratch.sprite_positions
|
||||
int error = 0;
|
||||
bool all_rendered = true;
|
||||
|
|
@ -761,7 +777,7 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPU
|
|||
}
|
||||
|
||||
ensure_canvas_can_fit(fg, num_cells + 1);
|
||||
bool was_colored = gpu_cells->attrs.width == 2 && is_emoji(cpu_cells->ch);
|
||||
bool was_colored = gpu_cells->attrs.width == 2 && is_emoji(cell_first_char(cpu_cells, tc));
|
||||
render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas.buf, fg->cell_width, fg->cell_height, num_cells, fg->baseline, &was_colored, (FONTS_DATA_HANDLE)fg, center_glyph);
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
|
||||
|
|
@ -804,28 +820,23 @@ typedef struct {
|
|||
|
||||
static GroupState group_state = {0};
|
||||
|
||||
static unsigned int
|
||||
num_codepoints_in_cell(CPUCell *cell) {
|
||||
unsigned int ans = 1;
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++;
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font, Font *fobj, bool disable_ligature) {
|
||||
shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font, Font *fobj, bool disable_ligature, const TextCache *tc) {
|
||||
if (group_state.groups_capacity <= 2 * num_cells) {
|
||||
group_state.groups_capacity = MAX(128u, 2 * num_cells); // avoid unnecessary reallocs
|
||||
group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity);
|
||||
if (!group_state.groups) fatal("Out of memory");
|
||||
}
|
||||
RAII_ListOfChars(lc);
|
||||
text_in_cell(first_cpu_cell, tc, &lc);
|
||||
group_state.previous_cluster = UINT32_MAX;
|
||||
group_state.prev_was_special = false;
|
||||
group_state.prev_was_empty = false;
|
||||
group_state.current_cell_data.cpu_cell = first_cpu_cell;
|
||||
group_state.current_cell_data.gpu_cell = first_gpu_cell;
|
||||
group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cpu_cell);
|
||||
group_state.current_cell_data.num_codepoints = MAX(1, lc.count);
|
||||
group_state.current_cell_data.codepoints_consumed = 0;
|
||||
group_state.current_cell_data.current_codepoint = first_cpu_cell->ch;
|
||||
group_state.current_cell_data.current_codepoint = lc.chars[0];
|
||||
zero_at_ptr_count(group_state.groups, group_state.groups_capacity);
|
||||
group_state.group_idx = 0;
|
||||
group_state.glyph_idx = 0;
|
||||
|
|
@ -835,7 +846,7 @@ shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb
|
|||
group_state.first_gpu_cell = first_gpu_cell;
|
||||
group_state.last_cpu_cell = first_cpu_cell + (num_cells ? num_cells - 1 : 0);
|
||||
group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0);
|
||||
load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells);
|
||||
load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells, tc);
|
||||
|
||||
size_t num_features = fobj->num_ffs_hb_features;
|
||||
if (num_features && !disable_ligature) num_features--; // the last feature is always -calt
|
||||
|
|
@ -877,7 +888,7 @@ is_empty_glyph(glyph_index glyph_id, Font *font) {
|
|||
}
|
||||
|
||||
static unsigned int
|
||||
check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell) {
|
||||
check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell, const TextCache *tc) {
|
||||
cell_data->codepoints_consumed++;
|
||||
if (cell_data->codepoints_consumed >= cell_data->num_codepoints) {
|
||||
uint16_t width = cell_data->gpu_cell->attrs.width;
|
||||
|
|
@ -885,20 +896,22 @@ check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell) {
|
|||
cell_data->gpu_cell += MAX(1, width);
|
||||
cell_data->codepoints_consumed = 0;
|
||||
if (cell_data->cpu_cell <= last_cpu_cell) {
|
||||
cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell);
|
||||
cell_data->current_codepoint = cell_data->cpu_cell->ch;
|
||||
cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell, tc);
|
||||
cell_data->current_codepoint = cell_first_char(cell_data->cpu_cell, tc);
|
||||
} else cell_data->current_codepoint = 0;
|
||||
return width;
|
||||
} else {
|
||||
switch(cell_data->codepoints_consumed) {
|
||||
case 0:
|
||||
cell_data->current_codepoint = cell_data->cpu_cell->ch;
|
||||
cell_data->current_codepoint = cell_first_char(cell_data->cpu_cell, tc);
|
||||
break;
|
||||
default: {
|
||||
index_type mark = cell_data->cpu_cell->cc_idx[cell_data->codepoints_consumed - 1];
|
||||
RAII_ListOfChars(lc);
|
||||
text_in_cell(cell_data->cpu_cell, tc, &lc);
|
||||
char_type cc = lc.chars[cell_data->codepoints_consumed];
|
||||
// VS15/16 cause rendering to break, as they get marked as
|
||||
// special glyphs, so map to 0, to avoid that
|
||||
cell_data->current_codepoint = (mark == VS15 || mark == VS16) ? 0 : codepoint_for_mark(mark);
|
||||
cell_data->current_codepoint = (cc == VS15 || cc == VS16) ? 0 : cc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -927,11 +940,12 @@ ligature_type_from_glyph_name(const char *glyph_name, SpacerStrategy strategy) {
|
|||
#define G(x) (group_state.x)
|
||||
|
||||
static void
|
||||
detect_spacer_strategy(hb_font_t *hbf, Font *font) {
|
||||
CPUCell cpu_cells[3] = {{.ch = '='}, {.ch = '='}, {.ch = '='}};
|
||||
detect_spacer_strategy(hb_font_t *hbf, Font *font, const TextCache *tc) {
|
||||
CPUCell cpu_cells[3] = {0};
|
||||
for (unsigned i = 0; i < arraysz(cpu_cells); i++) cell_set_char(&cpu_cells[i], '=');
|
||||
const CellAttrs w1 = {.width=1};
|
||||
GPUCell gpu_cells[3] = {{.attrs = w1}, {.attrs = w1}, {.attrs = w1}};
|
||||
shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false);
|
||||
shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false, tc);
|
||||
font->spacer_strategy = SPACERS_BEFORE;
|
||||
if (G(num_glyphs) > 1) {
|
||||
glyph_index glyph_id = G(info)[G(num_glyphs) - 1].codepoint;
|
||||
|
|
@ -939,7 +953,7 @@ detect_spacer_strategy(hb_font_t *hbf, Font *font) {
|
|||
bool is_empty = is_special && is_empty_glyph(glyph_id, font);
|
||||
if (is_empty) font->spacer_strategy = SPACERS_AFTER;
|
||||
}
|
||||
shape(cpu_cells, gpu_cells, 2, hbf, font, false);
|
||||
shape(cpu_cells, gpu_cells, 2, hbf, font, false, tc);
|
||||
if (G(num_glyphs)) {
|
||||
char glyph_name[128]; glyph_name[arraysz(glyph_name)-1] = 0;
|
||||
for (unsigned i = 0; i < G(num_glyphs); i++) {
|
||||
|
|
@ -956,8 +970,8 @@ detect_spacer_strategy(hb_font_t *hbf, Font *font) {
|
|||
// If spacer_strategy is still default, check ### glyph to confirm strategy
|
||||
// https://github.com/kovidgoyal/kitty/issues/4721
|
||||
if (font->spacer_strategy == SPACERS_BEFORE) {
|
||||
cpu_cells[0].ch = '#'; cpu_cells[1].ch = '#'; cpu_cells[2].ch = '#';
|
||||
shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false);
|
||||
for (unsigned i = 0; i < arraysz(cpu_cells); i++) cell_set_char(&cpu_cells[i], '#');
|
||||
shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false, tc);
|
||||
if (G(num_glyphs) > 1) {
|
||||
glyph_index glyph_id = G(info)[G(num_glyphs) - 1].codepoint;
|
||||
bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));
|
||||
|
|
@ -1000,7 +1014,7 @@ static LigatureType *ligature_types = NULL;
|
|||
static size_t ligature_types_sz = 0;
|
||||
|
||||
static void
|
||||
group_iosevka(Font *font, hb_font_t *hbf) {
|
||||
group_iosevka(Font *font, hb_font_t *hbf, const TextCache *tc) {
|
||||
// Group as per algorithm discussed in: https://github.com/be5invis/Iosevka/issues/1007
|
||||
if (ligature_types_sz <= G(num_glyphs)) {
|
||||
ligature_types_sz = G(num_glyphs) + 16;
|
||||
|
|
@ -1054,7 +1068,7 @@ group_iosevka(Font *font, hb_font_t *hbf) {
|
|||
} else {
|
||||
unsigned int num_cells_consumed = 0;
|
||||
while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {
|
||||
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell));
|
||||
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell), tc);
|
||||
G(cell_idx) += w;
|
||||
num_cells_consumed += w;
|
||||
num_codepoints_used_by_glyph--;
|
||||
|
|
@ -1067,7 +1081,7 @@ group_iosevka(Font *font, hb_font_t *hbf) {
|
|||
}
|
||||
|
||||
static void
|
||||
group_normal(Font *font, hb_font_t *hbf) {
|
||||
group_normal(Font *font, hb_font_t *hbf, const TextCache *tc) {
|
||||
/* Now distribute the glyphs into groups of cells
|
||||
* Considerations to keep in mind:
|
||||
* Group sizes should be as small as possible for best performance
|
||||
|
|
@ -1154,7 +1168,7 @@ group_normal(Font *font, hb_font_t *hbf) {
|
|||
} else {
|
||||
unsigned int num_cells_consumed = 0;
|
||||
while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {
|
||||
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell));
|
||||
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell), tc);
|
||||
G(cell_idx) += w;
|
||||
num_cells_consumed += w;
|
||||
num_codepoints_used_by_glyph--;
|
||||
|
|
@ -1177,12 +1191,12 @@ group_normal(Font *font, hb_font_t *hbf) {
|
|||
|
||||
|
||||
static void
|
||||
shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, bool disable_ligature) {
|
||||
shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, bool disable_ligature, const TextCache *tc) {
|
||||
hb_font_t *hbf = harfbuzz_font_for_face(font->face);
|
||||
if (font->spacer_strategy == SPACER_STRATEGY_UNKNOWN) detect_spacer_strategy(hbf, font);
|
||||
shape(first_cpu_cell, first_gpu_cell, num_cells, hbf, font, disable_ligature);
|
||||
if (font->spacer_strategy == SPACERS_IOSEVKA) group_iosevka(font, hbf);
|
||||
else group_normal(font, hbf);
|
||||
if (font->spacer_strategy == SPACER_STRATEGY_UNKNOWN) detect_spacer_strategy(hbf, font, tc);
|
||||
shape(first_cpu_cell, first_gpu_cell, num_cells, hbf, font, disable_ligature, tc);
|
||||
if (font->spacer_strategy == SPACERS_IOSEVKA) group_iosevka(font, hbf, tc);
|
||||
else group_normal(font, hbf, tc);
|
||||
#if 0
|
||||
static char dbuf[1024];
|
||||
// You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text
|
||||
|
|
@ -1228,7 +1242,7 @@ split_run_at_offset(index_type cursor_offset, index_type *left, index_type *righ
|
|||
|
||||
|
||||
static void
|
||||
render_groups(FontGroup *fg, Font *font, bool center_glyph) {
|
||||
render_groups(FontGroup *fg, Font *font, bool center_glyph, const TextCache *tc) {
|
||||
unsigned idx = 0;
|
||||
while (idx <= G(group_idx)) {
|
||||
Group *group = G(groups) + idx;
|
||||
|
|
@ -1244,7 +1258,7 @@ render_groups(FontGroup *fg, Font *font, bool center_glyph) {
|
|||
global_glyph_render_scratch.sz = sz;
|
||||
}
|
||||
for (unsigned i = 0; i < group->num_glyphs; i++) global_glyph_render_scratch.glyphs[i] = G(info)[group->first_glyph_idx + i].codepoint;
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, global_glyph_render_scratch.glyphs, group->num_glyphs, center_glyph);
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, global_glyph_render_scratch.glyphs, group->num_glyphs, center_glyph, tc);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
|
@ -1257,7 +1271,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||
int index = 0;
|
||||
if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL;
|
||||
index_type num = 0;
|
||||
while(num < line->xnum && line->cpu_cells[num].ch) num += line->gpu_cells[num].attrs.width;
|
||||
while(num < line->xnum && cell_has_text(line->cpu_cells + num)) num += line->gpu_cells[num].attrs.width;
|
||||
PyObject *face = NULL;
|
||||
Font *font;
|
||||
if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; }
|
||||
|
|
@ -1271,7 +1285,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||
FontGroup *fg = font_groups;
|
||||
font = fg->fonts + fg->medium_font_idx;
|
||||
}
|
||||
shape_run(line->cpu_cells, line->gpu_cells, num, font, false);
|
||||
shape_run(line->cpu_cells, line->gpu_cells, num, font, false, line->text_cache);
|
||||
|
||||
PyObject *ans = PyList_New(0);
|
||||
unsigned int idx = 0;
|
||||
|
|
@ -1292,35 +1306,35 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||
#undef G
|
||||
|
||||
static void
|
||||
render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature, bool center_glyph, int cursor_offset, DisableLigature disable_ligature_strategy) {
|
||||
render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature, bool center_glyph, int cursor_offset, DisableLigature disable_ligature_strategy, const TextCache *tc) {
|
||||
switch(font_idx) {
|
||||
default:
|
||||
shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx], disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS);
|
||||
shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx], disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS, tc);
|
||||
if (pua_space_ligature) collapse_pua_space_ligature(num_cells);
|
||||
else if (cursor_offset > -1) { // false if DISABLE_LIGATURES_NEVER
|
||||
index_type left, right;
|
||||
split_run_at_offset(cursor_offset, &left, &right);
|
||||
if (right > left) {
|
||||
if (left) {
|
||||
shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[font_idx], false);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph);
|
||||
shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[font_idx], false, tc);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph, tc);
|
||||
}
|
||||
shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[font_idx], true);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph);
|
||||
shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[font_idx], true, tc);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph, tc);
|
||||
if (right < num_cells) {
|
||||
shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[font_idx], false);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph);
|
||||
shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[font_idx], false, tc);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph, tc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph);
|
||||
render_groups(fg, &fg->fonts[font_idx], center_glyph, tc);
|
||||
break;
|
||||
case BLANK_FONT:
|
||||
while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; }
|
||||
break;
|
||||
case BOX_FONT:
|
||||
while(num_cells--) { render_box_cell(fg, first_cpu_cell, first_gpu_cell); first_cpu_cell++; first_gpu_cell++; }
|
||||
while(num_cells--) { render_box_cell(fg, first_cpu_cell, first_gpu_cell, tc); first_cpu_cell++; first_gpu_cell++; }
|
||||
break;
|
||||
case MISSING_FONT:
|
||||
while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; }
|
||||
|
|
@ -1352,11 +1366,11 @@ cell_cap_for_codepoint(const char_type cp) {
|
|||
|
||||
|
||||
void
|
||||
render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor, DisableLigature disable_ligature_strategy) {
|
||||
render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor, DisableLigature disable_ligature_strategy, ListOfChars *lc) {
|
||||
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) { \
|
||||
int cursor_offset = -1; \
|
||||
if (disable_ligature_at_cursor && first_cell_in_run <= cursor->x && cursor->x <= i) cursor_offset = cursor->x - first_cell_in_run; \
|
||||
render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false, center_glyph, cursor_offset, disable_ligature_strategy); \
|
||||
render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false, center_glyph, cursor_offset, disable_ligature_strategy, line->text_cache); \
|
||||
}
|
||||
FontGroup *fg = (FontGroup*)fg_;
|
||||
ssize_t run_font_idx = NO_FONT;
|
||||
|
|
@ -1368,30 +1382,32 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor,
|
|||
if (prev_width == 2) { prev_width = 0; continue; }
|
||||
CPUCell *cpu_cell = line->cpu_cells + i;
|
||||
GPUCell *gpu_cell = line->gpu_cells + i;
|
||||
text_in_cell(cpu_cell, line->text_cache, lc);
|
||||
bool is_main_font, is_emoji_presentation;
|
||||
ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell, &is_main_font, &is_emoji_presentation);
|
||||
ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell, &is_main_font, &is_emoji_presentation, line->text_cache, lc);
|
||||
const char_type first_ch = lc->chars[0];
|
||||
|
||||
if (
|
||||
cell_font_idx != MISSING_FONT &&
|
||||
((!is_main_font && !is_emoji_presentation && is_symbol(cpu_cell->ch)) || (cell_font_idx != BOX_FONT && (is_private_use(cpu_cell->ch))) || is_non_emoji_dingbat(cpu_cell->ch))
|
||||
((!is_main_font && !is_emoji_presentation && is_symbol(first_ch)) || (cell_font_idx != BOX_FONT && (is_private_use(first_ch))) || is_non_emoji_dingbat(first_ch))
|
||||
) {
|
||||
unsigned int desired_cells = 1;
|
||||
if (cell_font_idx > 0) {
|
||||
Font *font = (fg->fonts + cell_font_idx);
|
||||
glyph_index glyph_id = glyph_id_for_codepoint(font->face, cpu_cell->ch);
|
||||
glyph_index glyph_id = glyph_id_for_codepoint(font->face, first_ch);
|
||||
|
||||
int width = get_glyph_width(font->face, glyph_id);
|
||||
desired_cells = (unsigned int)ceilf((float)width / fg->cell_width);
|
||||
}
|
||||
desired_cells = MIN(desired_cells, cell_cap_for_codepoint(cpu_cell->ch));
|
||||
desired_cells = MIN(desired_cells, cell_cap_for_codepoint(first_ch));
|
||||
|
||||
unsigned int num_spaces = 0;
|
||||
while (
|
||||
i + num_spaces + 1 < line->xnum
|
||||
&& (line->cpu_cells[i+num_spaces+1].ch == ' ' || line->cpu_cells[i+num_spaces+1].ch == 0x2002) // space or en-space
|
||||
&& (cell_is_char(line->cpu_cells + i + num_spaces + 1, ' ') || cell_is_char(line->cpu_cells + i + num_spaces + 1, 0x2002)) // space or en-space
|
||||
&& num_spaces < MAX_NUM_EXTRA_GLYPHS_PUA
|
||||
&& num_spaces + 1 < desired_cells
|
||||
) {
|
||||
) {
|
||||
num_spaces++;
|
||||
// We have a private use char followed by space(s), render it as a multi-cell ligature.
|
||||
GPUCell *space_cell = line->gpu_cells + i + num_spaces;
|
||||
|
|
@ -1406,7 +1422,7 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor,
|
|||
center_glyph = true;
|
||||
RENDER
|
||||
center_glyph = false;
|
||||
render_run(fg, line->cpu_cells + i, line->gpu_cells + i, num_spaces + 1, cell_font_idx, true, center_glyph, -1, disable_ligature_strategy);
|
||||
render_run(fg, line->cpu_cells + i, line->gpu_cells + i, num_spaces + 1, cell_font_idx, true, center_glyph, -1, disable_ligature_strategy, line->text_cache);
|
||||
run_font_idx = NO_FONT;
|
||||
first_cell_in_run = i + num_spaces + 1;
|
||||
prev_width = line->gpu_cells[i+num_spaces].attrs.width;
|
||||
|
|
@ -1622,7 +1638,8 @@ test_render_line(PyObject UNUSED *self, PyObject *args) {
|
|||
PyObject *line;
|
||||
if (!PyArg_ParseTuple(args, "O!", &Line_Type, &line)) return NULL;
|
||||
if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
|
||||
render_line((FONTS_DATA_HANDLE)font_groups, (Line*)line, 0, NULL, DISABLE_LIGATURES_NEVER);
|
||||
RAII_ListOfChars(lc);
|
||||
render_line((FONTS_DATA_HANDLE)font_groups, (Line*)line, 0, NULL, DISABLE_LIGATURES_NEVER, &lc);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -1710,16 +1727,13 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
|||
PyObject *text;
|
||||
int bold, italic;
|
||||
if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
|
||||
CPUCell cpu_cell = {0};
|
||||
GPUCell gpu_cell = {0};
|
||||
static Py_UCS4 char_buf[2 + arraysz(cpu_cell.cc_idx)];
|
||||
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
||||
cpu_cell.ch = char_buf[0];
|
||||
for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cpu_cell.cc_idx); i++) cpu_cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
||||
RAII_ListOfChars(lc); ensure_space_for_chars(&lc, PyUnicode_GetLength(text));
|
||||
if (!PyUnicode_AsUCS4(text, lc.chars, lc.count, 1)) return NULL;
|
||||
if (bold) gpu_cell.attrs.bold = true;
|
||||
if (italic) gpu_cell.attrs.italic = true;
|
||||
FontGroup *fg = font_groups;
|
||||
ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell);
|
||||
ssize_t ans = fallback_font(fg, &gpu_cell, &lc);
|
||||
if (ans == MISSING_FONT) { PyErr_SetString(PyExc_ValueError, "No fallback font found"); return NULL; }
|
||||
if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
||||
return fg->fonts[ans].face;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*);
|
|||
bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE);
|
||||
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE, bool center_glyph);
|
||||
PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);
|
||||
PyObject* create_fallback_face(PyObject *base_face, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);
|
||||
PyObject* specialize_font_descriptor(PyObject *base_descriptor, double, double, double);
|
||||
PyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE);
|
||||
PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE);
|
||||
|
|
@ -51,7 +51,7 @@ const char* postscript_name_for_face(const PyObject*);
|
|||
|
||||
void sprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned int *y, unsigned int *z);
|
||||
void render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb);
|
||||
void render_line(FONTS_DATA_HANDLE, Line *line, index_type lnum, Cursor *cursor, DisableLigature);
|
||||
void render_line(FONTS_DATA_HANDLE, Line *line, index_type lnum, Cursor *cursor, DisableLigature, ListOfChars*);
|
||||
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
||||
typedef void (*free_extra_data_func)(void*);
|
||||
StringCanvas render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline);
|
||||
|
|
@ -74,7 +74,7 @@ bool create_features_for_face(const char* psname, PyObject *features, FontFeatur
|
|||
PyObject*
|
||||
font_features_as_dict(const FontFeatures *font_features);
|
||||
bool
|
||||
has_cell_text(bool(*has_codepoint)(const void*, char_type ch), const void* face, const CPUCell *cell, bool do_debug);
|
||||
has_cell_text(bool(*has_codepoint)(const void*, char_type ch), const void* face, bool do_debug, const ListOfChars *lc);
|
||||
|
||||
static inline void
|
||||
right_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) {
|
||||
|
|
|
|||
|
|
@ -602,7 +602,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo
|
|||
first--;
|
||||
CPUCell *cells = cpu_lineptr(self, self->line_map[first]);
|
||||
for(i = 0; i < self->xnum; i++) {
|
||||
if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; }
|
||||
if (cells[i].ch_or_idx || cells[i].ch_is_idx) { is_empty = false; break; }
|
||||
}
|
||||
} while(is_empty && first > 0);
|
||||
|
||||
|
|
|
|||
269
kitty/line.c
269
kitty/line.c
|
|
@ -12,6 +12,7 @@
|
|||
#include "wcwidth-std.h"
|
||||
|
||||
extern PyTypeObject Cursor_Type;
|
||||
static_assert(sizeof(char_type) == sizeof(Py_UCS4), "Need to perform conversion to Py_UCS4");
|
||||
|
||||
static void
|
||||
dealloc(Line* self) {
|
||||
|
|
@ -23,24 +24,35 @@ dealloc(Line* self) {
|
|||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
text_in_cell_ansi(const CPUCell *c, TextCache *tc, ANSIBuf *output) {
|
||||
if (c->ch_is_idx) return tc_chars_at_index_ansi(tc, c->ch_or_idx, output);
|
||||
ensure_space_for(output, buf, output->buf[0], output->len + 1, capacity, 2048, false);
|
||||
output->buf[output->len++] = c->ch_or_idx;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
line_length(Line *self) {
|
||||
index_type last = self->xnum - 1;
|
||||
for (index_type i = 0; i < self->xnum; i++) {
|
||||
if ((self->cpu_cells[last - i].ch) != BLANK_CHAR) return self->xnum - i;
|
||||
if (!cell_is_char(self->cpu_cells + last - i, BLANK_CHAR)) return self->xnum - i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
cell_text(CPUCell *cell) {
|
||||
PyObject *ans;
|
||||
unsigned num = 1;
|
||||
static Py_UCS4 buf[arraysz(cell->cc_idx) + 1];
|
||||
buf[0] = cell->ch;
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) buf[num++] = codepoint_for_mark(cell->cc_idx[i]);
|
||||
ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, num);
|
||||
return ans;
|
||||
cell_text(CPUCell *cell, TextCache *tc) {
|
||||
if (cell->ch_is_idx) {
|
||||
RAII_ListOfChars(lc);
|
||||
tc_chars_at_index(tc, cell->ch_or_idx, &lc);
|
||||
PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, &lc.chars, lc.count);
|
||||
return ans;
|
||||
} else {
|
||||
Py_UCS4 ch = cell->ch_or_idx;
|
||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, &ch, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// URL detection {{{
|
||||
|
|
@ -50,8 +62,21 @@ is_hostname_char(char_type ch) {
|
|||
return ch == '[' || ch == ']' || is_url_char(ch);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_hostname_lc(const ListOfChars *lc) {
|
||||
for (size_t i = 0; i < lc->count; i++) if (!is_hostname_char(lc->chars[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_url_lc(const ListOfChars *lc) {
|
||||
for (size_t i = 0; i < lc->count; i++) if (!is_url_char(lc->chars[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static index_type
|
||||
find_colon_slash(Line *self, index_type x, index_type limit) {
|
||||
find_colon_slash(Line *self, index_type x, index_type limit, ListOfChars *lc) {
|
||||
// Find :// at or before x
|
||||
index_type pos = MIN(x, self->xnum - 1);
|
||||
enum URL_PARSER_STATES {ANY, FIRST_SLASH, SECOND_SLASH};
|
||||
|
|
@ -59,25 +84,26 @@ find_colon_slash(Line *self, index_type x, index_type limit) {
|
|||
limit = MAX(2u, limit);
|
||||
if (pos < limit) return 0;
|
||||
do {
|
||||
char_type ch = self->cpu_cells[pos].ch;
|
||||
if (!is_hostname_char(ch)) return false;
|
||||
const CPUCell *c = self->cpu_cells + pos;
|
||||
text_in_cell(c, self->text_cache, lc);
|
||||
if (!is_hostname_lc(lc)) return false;
|
||||
if (pos == x) {
|
||||
if (ch == ':') {
|
||||
if (pos + 2 < self->xnum && self->cpu_cells[pos+1].ch == '/' && self->cpu_cells[pos + 2].ch == '/') state = SECOND_SLASH;
|
||||
} else if (ch == '/') {
|
||||
if (pos + 1 < self->xnum && self->cpu_cells[pos+1].ch == '/') state = FIRST_SLASH;
|
||||
if (cell_is_char(c, ':')) {
|
||||
if (pos + 2 < self->xnum && cell_is_char(self->cpu_cells + pos + 1, '/') && cell_is_char(self->cpu_cells + pos + 2, '/')) state = SECOND_SLASH;
|
||||
} else if (cell_is_char(c, '/')) {
|
||||
if (pos + 1 < self->xnum && cell_is_char(self->cpu_cells + pos + 1, '/')) state = FIRST_SLASH;
|
||||
}
|
||||
}
|
||||
switch(state) {
|
||||
case ANY:
|
||||
if (ch == '/') state = FIRST_SLASH;
|
||||
if (cell_is_char(c, '/')) state = FIRST_SLASH;
|
||||
break;
|
||||
case FIRST_SLASH:
|
||||
state = ch == '/' ? SECOND_SLASH : ANY;
|
||||
state = cell_is_char(c, '/') ? SECOND_SLASH : ANY;
|
||||
break;
|
||||
case SECOND_SLASH:
|
||||
if (ch == ':') return pos;
|
||||
state = ch == '/' ? SECOND_SLASH : ANY;
|
||||
if (cell_is_char(c, ':')) return pos;
|
||||
state = cell_is_char(c, '/') ? SECOND_SLASH : ANY;
|
||||
break;
|
||||
}
|
||||
pos--;
|
||||
|
|
@ -90,7 +116,7 @@ prefix_matches(Line *self, index_type at, const char_type* prefix, index_type pr
|
|||
if (prefix_len > at) return false;
|
||||
index_type p, i;
|
||||
for (p = at - prefix_len, i = 0; i < prefix_len && p < self->xnum; i++, p++) {
|
||||
if ((self->cpu_cells[p].ch) != prefix[i]) return false;
|
||||
if (!cell_is_char(self->cpu_cells + p, prefix[i])) return false;
|
||||
}
|
||||
return i == prefix_len;
|
||||
}
|
||||
|
|
@ -108,15 +134,18 @@ has_url_prefix_at(Line *self, index_type at, index_type min_prefix_len, index_ty
|
|||
#define MIN_URL_LEN 5
|
||||
|
||||
static bool
|
||||
has_url_beyond_colon_slash(Line *self, index_type x) {
|
||||
has_url_beyond_colon_slash(Line *self, index_type x, ListOfChars *lc) {
|
||||
unsigned num_of_slashes = 0;
|
||||
for (index_type i = x; i < MIN(x + MIN_URL_LEN + 3, self->xnum); i++) {
|
||||
const char_type ch = self->cpu_cells[i].ch;
|
||||
const CPUCell *c = self->cpu_cells + i;
|
||||
text_in_cell(c, self->text_cache, lc);
|
||||
if (num_of_slashes < 3) {
|
||||
if (!is_hostname_char(ch)) return false;
|
||||
if (ch == '/') num_of_slashes++;
|
||||
if (!is_hostname_lc(lc)) return false;
|
||||
if (lc->count == 1 && lc->chars[0] == '/') num_of_slashes++;
|
||||
}
|
||||
else {
|
||||
for (size_t n = 0; n < lc->count; n++) if (!is_url_char(lc->chars[n])) return false;
|
||||
}
|
||||
else { if (!is_url_char(ch)) return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -127,34 +156,37 @@ line_url_start_at(Line *self, index_type x) {
|
|||
// known-prefix://url-chars. If no URL is found self->xnum is returned.
|
||||
if (x >= self->xnum || self->xnum <= MIN_URL_LEN + 3) return self->xnum;
|
||||
index_type ds_pos = 0, t;
|
||||
RAII_ListOfChars(lc);
|
||||
// First look for :// ahead of x
|
||||
ds_pos = find_colon_slash(self, x + OPT(url_prefixes).max_prefix_len + 3, x < 2 ? 0 : x - 2);
|
||||
if (ds_pos != 0 && has_url_beyond_colon_slash(self, ds_pos)) {
|
||||
ds_pos = find_colon_slash(self, x + OPT(url_prefixes).max_prefix_len + 3, x < 2 ? 0 : x - 2, &lc);
|
||||
if (ds_pos != 0 && has_url_beyond_colon_slash(self, ds_pos, &lc)) {
|
||||
if (has_url_prefix_at(self, ds_pos, ds_pos > x ? ds_pos - x: 0, &t)) return t;
|
||||
}
|
||||
ds_pos = find_colon_slash(self, x, 0);
|
||||
if (ds_pos == 0 || self->xnum < ds_pos + MIN_URL_LEN + 3 || !has_url_beyond_colon_slash(self, ds_pos)) return self->xnum;
|
||||
ds_pos = find_colon_slash(self, x, 0, &lc);
|
||||
if (ds_pos == 0 || self->xnum < ds_pos + MIN_URL_LEN + 3 || !has_url_beyond_colon_slash(self, ds_pos, &lc)) return self->xnum;
|
||||
if (has_url_prefix_at(self, ds_pos, 0, &t)) return t;
|
||||
return self->xnum;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_pos_ok_for_url(Line *self, index_type x, bool in_hostname, index_type last_hostname_char_pos) {
|
||||
is_pos_ok_for_url(Line *self, index_type x, bool in_hostname, index_type last_hostname_char_pos, ListOfChars *lc) {
|
||||
if (x >= self->xnum) return false;
|
||||
if (in_hostname && x <= last_hostname_char_pos) return is_hostname_char(self->cpu_cells[x].ch);
|
||||
return is_url_char(self->cpu_cells[x].ch);
|
||||
text_in_cell(self->cpu_cells + x, self->text_cache, lc);
|
||||
if (in_hostname && x <= last_hostname_char_pos) return is_hostname_lc(lc);
|
||||
return is_url_lc(lc);
|
||||
}
|
||||
|
||||
index_type
|
||||
line_url_end_at(Line *self, index_type x, bool check_short, char_type sentinel, bool next_line_starts_with_url_chars, bool in_hostname, index_type last_hostname_char_pos) {
|
||||
index_type ans = x;
|
||||
if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3)) return 0;
|
||||
#define pos_ok(x) is_pos_ok_for_url(self, x, in_hostname, last_hostname_char_pos)
|
||||
if (sentinel) { while (ans < self->xnum && self->cpu_cells[ans].ch != sentinel && pos_ok(ans)) ans++; }
|
||||
RAII_ListOfChars(lc);
|
||||
#define pos_ok(x) is_pos_ok_for_url(self, x, in_hostname, last_hostname_char_pos, &lc)
|
||||
if (sentinel) { while (ans < self->xnum && !cell_is_char(self->cpu_cells + ans, sentinel) && pos_ok(ans)) ans++; }
|
||||
else { while (ans < self->xnum && pos_ok(ans)) ans++; }
|
||||
if (ans) ans--;
|
||||
if (ans < self->xnum - 1 || !next_line_starts_with_url_chars) {
|
||||
while (ans > x && can_strip_from_end_of_url(self->cpu_cells[ans].ch)) ans--;
|
||||
while (ans > x && !self->cpu_cells[ans].ch_is_idx && can_strip_from_end_of_url(self->cpu_cells[ans].ch_or_idx)) ans--;
|
||||
}
|
||||
#undef pos_ok
|
||||
return ans;
|
||||
|
|
@ -162,8 +194,10 @@ line_url_end_at(Line *self, index_type x, bool check_short, char_type sentinel,
|
|||
|
||||
bool
|
||||
line_startswith_url_chars(Line *self, bool in_hostname) {
|
||||
if (in_hostname) return is_hostname_char(self->cpu_cells[0].ch);
|
||||
return is_url_char(self->cpu_cells[0].ch);
|
||||
RAII_ListOfChars(lc);
|
||||
text_in_cell(self->cpu_cells, self->text_cache, &lc);
|
||||
if (in_hostname) return is_hostname_lc(&lc);
|
||||
return is_url_lc(&lc);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -188,54 +222,41 @@ static PyObject*
|
|||
text_at(Line* self, Py_ssize_t xval) {
|
||||
#define text_at_doc "[x] -> Return the text in the specified cell"
|
||||
if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
|
||||
return cell_text(self->cpu_cells + xval);
|
||||
return cell_text(self->cpu_cells + xval, self->text_cache);
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) {
|
||||
static size_t
|
||||
cell_as_unicode(ListOfChars *lc, bool include_cc, Py_UCS4 *buf, char_type zero_char) {
|
||||
size_t n = 1;
|
||||
buf[0] = cell->ch ? cell->ch : zero_char;
|
||||
if (include_cc) {
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) buf[n++] = codepoint_for_mark(cell->cc_idx[i]);
|
||||
}
|
||||
buf[0] = lc->chars[0] ? lc->chars[0] : zero_char;
|
||||
if (include_cc && lc->count > 1) memcpy(buf + 1, lc->chars + 1, lc->count - 1);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_unicode_for_fallback(CPUCell *cell, Py_UCS4 *buf) {
|
||||
cell_as_unicode_for_fallback(const ListOfChars *lc, Py_UCS4 *buf) {
|
||||
size_t n = 1;
|
||||
buf[0] = cell->ch ? cell->ch : ' ';
|
||||
buf[0] = lc->chars[0] ? lc->chars[0] : ' ';
|
||||
if (buf[0] != '\t') {
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
if (cell->cc_idx[i] != VS15 && cell->cc_idx[i] != VS16) buf[n++] = codepoint_for_mark(cell->cc_idx[i]);
|
||||
for (unsigned i = 1; i < lc->count; i++) {
|
||||
if (lc->chars[i] != VS15 && lc->chars[i] != VS16) buf[n++] = lc->chars[i];
|
||||
}
|
||||
} else buf[0] = ' ';
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type zero_char) {
|
||||
char_type ch = cell->ch ? cell->ch : zero_char;
|
||||
if (ch == '\t') { include_cc = false; }
|
||||
size_t n = encode_utf8(ch, buf);
|
||||
if (include_cc) {
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) n += encode_utf8(codepoint_for_mark(cell->cc_idx[i]), buf + n);
|
||||
}
|
||||
buf[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_utf8_for_fallback(CPUCell *cell, char *buf) {
|
||||
char_type ch = cell->ch ? cell->ch : ' ';
|
||||
cell_as_utf8_for_fallback(CPUCell *cell, TextCache *tc, char *buf) {
|
||||
RAII_ListOfChars(lc);
|
||||
text_in_cell(cell, tc, &lc);
|
||||
char_type ch = lc.chars[0] ? lc.chars[0] : ' ';
|
||||
bool include_cc = true;
|
||||
if (ch == '\t') { ch = ' '; include_cc = false; }
|
||||
size_t n = encode_utf8(ch, buf);
|
||||
if (include_cc) {
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
if (cell->cc_idx[i] != VS15 && cell->cc_idx[i] != VS16) {
|
||||
n += encode_utf8(codepoint_for_mark(cell->cc_idx[i]), buf + n);
|
||||
}
|
||||
for (unsigned i = 1; i < lc.count; i++) {
|
||||
char_type ch = lc.chars[i];
|
||||
if (ch != VS15 && ch != VS16) n += encode_utf8(ch, buf + n);
|
||||
}
|
||||
}
|
||||
buf[n] = 0;
|
||||
|
|
@ -243,33 +264,32 @@ cell_as_utf8_for_fallback(CPUCell *cell, char *buf) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
PyObject*
|
||||
unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells) {
|
||||
size_t n = 0;
|
||||
static Py_UCS4 buf[4096];
|
||||
RAII_ListOfChars(lc);
|
||||
char_type previous_width = 0;
|
||||
for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cpu_cells->cc_idx); i++) {
|
||||
char_type ch = self->cpu_cells[i].ch;
|
||||
if (ch == 0) {
|
||||
for(index_type i = start; i < limit; i++) {
|
||||
text_in_cell(self->cpu_cells + i, self->text_cache, &lc);
|
||||
if (!lc.chars[0]) {
|
||||
if (previous_width == 2) { previous_width = 0; continue; };
|
||||
if (skip_zero_cells) continue;
|
||||
}
|
||||
if (ch == '\t') {
|
||||
if (lc.count + n >= arraysz(buf)) break;
|
||||
if (lc.chars[0] == '\t') {
|
||||
buf[n++] = '\t';
|
||||
unsigned num_cells_to_skip_for_tab = self->cpu_cells[i].cc_idx[0];
|
||||
while (num_cells_to_skip_for_tab && i + 1 < limit && self->cpu_cells[i+1].ch == ' ') {
|
||||
unsigned num_cells_to_skip_for_tab = lc.count > 1 ? lc.chars[1] : 0;
|
||||
while (num_cells_to_skip_for_tab && i + 1 < limit && cell_is_char(self->cpu_cells+i+1, ' ')) {
|
||||
i++;
|
||||
num_cells_to_skip_for_tab--;
|
||||
}
|
||||
} else {
|
||||
n += cell_as_unicode(self->cpu_cells + i, include_cc, buf + n, ' ');
|
||||
n += cell_as_unicode(&lc, include_cc, buf + n, ' ');
|
||||
}
|
||||
previous_width = self->gpu_cells[i].attrs.width;
|
||||
}
|
||||
if (add_trailing_newline && !self->gpu_cells[self->xnum-1].attrs.next_char_was_wrapped && n < arraysz(buf)) {
|
||||
buf[n++] = '\n';
|
||||
}
|
||||
if (add_trailing_newline && !self->gpu_cells[self->xnum-1].attrs.next_char_was_wrapped && n < arraysz(buf)) buf[n++] = '\n';
|
||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +351,7 @@ write_mark(const char *mark, ANSIBuf *output) {
|
|||
|
||||
bool
|
||||
line_as_ansi(Line *self, ANSIBuf *output, const GPUCell** prev_cell, index_type start_at, index_type stop_before, char_type prefix_char) {
|
||||
#define ENSURE_SPACE(extra) ensure_space_for(output, buf, Py_UCS4, output->len + extra, capacity, 2048, false);
|
||||
#define ENSURE_SPACE(extra) ensure_space_for(output, buf, output->buf[0], output->len + extra, capacity, 2048, false);
|
||||
#define WRITE_SGR(val) { ENSURE_SPACE(128); escape_code_written = true; write_sgr(val, output); }
|
||||
#define WRITE_CH(val) { ENSURE_SPACE(1); output->buf[output->len++] = val; }
|
||||
#define WRITE_HYPERLINK(val) { ENSURE_SPACE(2256); escape_code_written = true; write_hyperlink(val, output); }
|
||||
|
|
@ -363,10 +383,10 @@ line_as_ansi(Line *self, ANSIBuf *output, const GPUCell** prev_cell, index_type
|
|||
const CellAttrs mask_for_sgr = {.val=SGR_MASK};
|
||||
|
||||
for (index_type pos=start_at; pos < limit; pos++) {
|
||||
char_type ch = self->cpu_cells[pos].ch;
|
||||
if (ch == 0) {
|
||||
if (previous_width == 2) { previous_width = 0; continue; }
|
||||
ch = ' ';
|
||||
unsigned n = text_in_cell_ansi(self->cpu_cells + pos, self->text_cache, output);
|
||||
if (output->buf[output->len - n] == 0) {
|
||||
if (previous_width == 2) { previous_width = 0; output->len -= n; continue; }
|
||||
output->buf[output->len - n] = ' ';
|
||||
}
|
||||
if (output->hyperlink_pool) {
|
||||
hyperlink_id_type hid = self->cpu_cells[pos].hyperlink_id;
|
||||
|
|
@ -384,15 +404,14 @@ line_as_ansi(Line *self, ANSIBuf *output, const GPUCell** prev_cell, index_type
|
|||
if (*sgr) WRITE_SGR(sgr);
|
||||
}
|
||||
*prev_cell = cell;
|
||||
WRITE_CH(ch);
|
||||
if (ch == '\t') {
|
||||
unsigned num_cells_to_skip_for_tab = self->cpu_cells[pos].cc_idx[0];
|
||||
while (num_cells_to_skip_for_tab && pos + 1 < limit && self->cpu_cells[pos+1].ch == ' ') {
|
||||
num_cells_to_skip_for_tab--; pos++;
|
||||
if (output->buf[output->len - n] == '\t') {
|
||||
unsigned num_cells_to_skip_for_tab = 0;
|
||||
if (n > 1) {
|
||||
num_cells_to_skip_for_tab = output->buf[output->len - n + 1];
|
||||
output->len -= n - 1;
|
||||
}
|
||||
} else {
|
||||
for(unsigned c = 0; c < arraysz(self->cpu_cells[pos].cc_idx) && self->cpu_cells[pos].cc_idx[c]; c++) {
|
||||
WRITE_CH(codepoint_for_mark(self->cpu_cells[pos].cc_idx[c]));
|
||||
while (num_cells_to_skip_for_tab && pos + 1 < limit && cell_is_char(self->cpu_cells + pos + 1, ' ')) {
|
||||
num_cells_to_skip_for_tab--; pos++;
|
||||
}
|
||||
}
|
||||
previous_width = cell->attrs.width;
|
||||
|
|
@ -448,17 +467,19 @@ width(Line *self, PyObject *val) {
|
|||
return PyLong_FromUnsignedLong((unsigned long) (self->gpu_cells[x].attrs.width));
|
||||
}
|
||||
|
||||
void
|
||||
line_add_combining_char(CPUCell *cpu_cells, GPUCell *gpu_cells, uint32_t ch, unsigned int x) {
|
||||
bool
|
||||
line_add_combining_char(CPUCell *cpu_cells, GPUCell *gpu_cells, TextCache *tc, ListOfChars *lc, uint32_t ch, unsigned int x) {
|
||||
CPUCell *cell = cpu_cells + x;
|
||||
if (!cell->ch) {
|
||||
if (x > 0 && (gpu_cells[x-1].attrs.width) == 2 && cpu_cells[x-1].ch) cell = cpu_cells + x - 1;
|
||||
else return; // don't allow adding combining chars to a null cell
|
||||
if (!cell->ch_or_idx && !cell->ch_is_idx) {
|
||||
if (x > 0 && (gpu_cells[x-1].attrs.width) == 2 && cell_has_text(cpu_cells + x - 1)) cell = cpu_cells + x - 1;
|
||||
else return false; // don't allow adding combining chars to a null cell
|
||||
}
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx); i++) {
|
||||
if (!cell->cc_idx[i]) { cell->cc_idx[i] = mark_for_codepoint(ch); return; }
|
||||
}
|
||||
cell->cc_idx[arraysz(cell->cc_idx) - 1] = mark_for_codepoint(ch);
|
||||
text_in_cell(cell, tc, lc);
|
||||
ensure_space_for_chars(lc, lc->count + 1);
|
||||
lc->chars[lc->count++] = ch;
|
||||
cell->ch_or_idx = tc_get_or_insert_chars(tc, lc);
|
||||
cell->ch_is_idx = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
|
@ -471,7 +492,8 @@ add_combining_char(Line* self, PyObject *args) {
|
|||
PyErr_SetString(PyExc_ValueError, "Column index out of bounds");
|
||||
return NULL;
|
||||
}
|
||||
line_add_combining_char(self->cpu_cells, self->gpu_cells, new_char, x);
|
||||
RAII_ListOfChars(lc);
|
||||
line_add_combining_char(self->cpu_cells, self->gpu_cells, self->text_cache, &lc, new_char, x);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -502,13 +524,12 @@ set_text(Line* self, PyObject *args) {
|
|||
color_type dfg = cursor->decoration_fg & COL_MASK;
|
||||
|
||||
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->cpu_cells[i].val = 0;
|
||||
self->cpu_cells[i].ch_or_idx = PyUnicode_READ(kind, buf, offset);
|
||||
self->gpu_cells[i].attrs = attrs;
|
||||
self->gpu_cells[i].fg = fg;
|
||||
self->gpu_cells[i].bg = bg;
|
||||
self->gpu_cells[i].decoration_fg = dfg;
|
||||
memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
|
|
@ -537,7 +558,7 @@ cursor_from(Line* self, PyObject *args) {
|
|||
void
|
||||
line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) {
|
||||
const uint16_t width = ch ? 1 : 0;
|
||||
const CPUCell cc = {.ch=ch};
|
||||
const CPUCell cc = {.ch_or_idx=ch};
|
||||
if (at + num > self->xnum) num = self->xnum > at ? self->xnum - at : 0;
|
||||
memset_array(self->cpu_cells + at, cc, num);
|
||||
for (index_type i = at; i < at + num; i++) self->gpu_cells[i].attrs.width = width;
|
||||
|
|
@ -584,15 +605,16 @@ apply_cursor(Line* self, PyObject *args) {
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
void line_right_shift(Line *self, unsigned int at, unsigned int num) {
|
||||
void
|
||||
line_right_shift(Line *self, unsigned int at, unsigned int num) {
|
||||
for(index_type i = self->xnum - 1; i >= at + num; i--) {
|
||||
COPY_SELF_CELL(i - num, i)
|
||||
}
|
||||
// Check if a wide character was split at the right edge
|
||||
if (self->gpu_cells[self->xnum - 1].attrs.width != 1) {
|
||||
self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR;
|
||||
self->cpu_cells[self->xnum - 1].val = 0;
|
||||
self->cpu_cells[self->xnum - 1].hyperlink_id = 0;
|
||||
self->gpu_cells[self->xnum - 1].attrs = (CellAttrs){.width=BLANK_CHAR ? 1 : 0};
|
||||
self->gpu_cells[self->xnum - 1].attrs = (CellAttrs){0};
|
||||
clear_sprite_position(self->gpu_cells[self->xnum - 1]);
|
||||
}
|
||||
}
|
||||
|
|
@ -654,11 +676,18 @@ colors_for_cell(Line *self, const ColorProfile *cp, index_type *x, color_type *f
|
|||
|
||||
char_type
|
||||
line_get_char(Line *self, index_type at) {
|
||||
char_type ch = self->cpu_cells[at].ch;
|
||||
if (!ch && at > 0 && (self->gpu_cells[at-1].attrs.width) > 1) ch = self->cpu_cells[at-1].ch;
|
||||
return ch;
|
||||
if (self->cpu_cells[at].ch_is_idx) {
|
||||
RAII_ListOfChars(lc);
|
||||
text_in_cell(self->cpu_cells + at, self->text_cache, &lc);
|
||||
return lc.chars[0];
|
||||
} else {
|
||||
char_type ch = self->cpu_cells[at].ch_or_idx;
|
||||
if (!ch && at > 0 && (self->gpu_cells[at-1].attrs.width) > 1) ch = line_get_char(self, at - 1);
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, hyperlink_id_type hyperlink_id) {
|
||||
GPUCell *g = self->gpu_cells + at;
|
||||
|
|
@ -670,13 +699,12 @@ line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Curs
|
|||
g->bg = cursor->bg & COL_MASK;
|
||||
g->decoration_fg = cursor->decoration_fg & COL_MASK;
|
||||
}
|
||||
self->cpu_cells[at].ch = ch;
|
||||
cell_set_char(self->cpu_cells + at, ch);
|
||||
self->cpu_cells[at].hyperlink_id = hyperlink_id;
|
||||
if (OPT(underline_hyperlinks) == UNDERLINE_ALWAYS && hyperlink_id) {
|
||||
g->decoration_fg = ((OPT(url_color) & COL_MASK) << 8) | 2;
|
||||
g->attrs.decoration = OPT(url_style);
|
||||
}
|
||||
memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
|
@ -802,21 +830,20 @@ apply_mark(Line *line, const uint16_t mark, index_type *cell_pos, unsigned int *
|
|||
index_type x = *cell_pos;
|
||||
MARK;
|
||||
(*match_pos)++;
|
||||
if (line->cpu_cells[x].ch) {
|
||||
if (line->cpu_cells[x].ch == '\t') {
|
||||
unsigned num_cells_to_skip_for_tab = line->cpu_cells[x].cc_idx[0];
|
||||
while (num_cells_to_skip_for_tab && x + 1 < line->xnum && line->cpu_cells[x+1].ch == ' ') {
|
||||
RAII_ListOfChars(lc); text_in_cell(line->cpu_cells + x, line->text_cache, &lc);
|
||||
if (lc.chars[0]) {
|
||||
if (lc.chars[0] == '\t') {
|
||||
unsigned num_cells_to_skip_for_tab = lc.count > 1 ? lc.chars[1] : 0;
|
||||
while (num_cells_to_skip_for_tab && x + 1 < line->xnum && cell_is_char(line->cpu_cells+x+1, ' ')) {
|
||||
x++;
|
||||
num_cells_to_skip_for_tab--;
|
||||
MARK;
|
||||
}
|
||||
} else if ((line->gpu_cells[x].attrs.width) > 1 && x + 1 < line->xnum && !line->cpu_cells[x+1].ch) {
|
||||
} else if ((line->gpu_cells[x].attrs.width) > 1 && x + 1 < line->xnum && !cell_has_text(line->cpu_cells+x+1)) {
|
||||
x++;
|
||||
MARK;
|
||||
} else {
|
||||
for (index_type i = 0; i < arraysz(line->cpu_cells[x].cc_idx); i++) {
|
||||
if (line->cpu_cells[x].cc_idx[i]) (*match_pos)++;
|
||||
}
|
||||
*match_pos += lc.count - 1;
|
||||
}
|
||||
}
|
||||
*cell_pos = x + 1;
|
||||
|
|
|
|||
50
kitty/line.h
50
kitty/line.h
|
|
@ -28,6 +28,10 @@ typedef union CellAttrs {
|
|||
#define DECORATION_MASK (7u)
|
||||
#define NUM_UNDERLINE_STYLES (5u)
|
||||
#define SGR_MASK (~(((CellAttrs){.width=WIDTH_MASK, .mark=MARK_MASK, .next_char_was_wrapped=1}).val))
|
||||
// Text presentation selector
|
||||
#define VS15 0xfe0e
|
||||
// Emoji presentation selector
|
||||
#define VS16 0xfe0f
|
||||
|
||||
typedef struct {
|
||||
color_type fg, bg, decoration_fg;
|
||||
|
|
@ -36,13 +40,16 @@ typedef struct {
|
|||
} GPUCell;
|
||||
static_assert(sizeof(GPUCell) == 20, "Fix the ordering of GPUCell");
|
||||
|
||||
typedef struct {
|
||||
char_type ch;
|
||||
hyperlink_id_type hyperlink_id;
|
||||
combining_type cc_idx[3];
|
||||
typedef union CPUCell {
|
||||
struct {
|
||||
char_type ch_or_idx: sizeof(char_type) * 8;
|
||||
hyperlink_id_type hyperlink_id: sizeof(hyperlink_id_type) * 8;
|
||||
bool ch_is_idx: 1;
|
||||
uint16_t : 15;
|
||||
};
|
||||
uint64_t val;
|
||||
} CPUCell;
|
||||
static_assert(sizeof(CPUCell) == 12, "Fix the ordering of CPUCell");
|
||||
|
||||
static_assert(sizeof(CPUCell) == sizeof(uint64_t), "Fix the ordering of CPUCell");
|
||||
|
||||
typedef union LineAttrs {
|
||||
struct {
|
||||
|
|
@ -69,6 +76,37 @@ typedef struct {
|
|||
Line* alloc_line(TextCache *text_cache);
|
||||
void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, int *params, unsigned int count, bool is_group);
|
||||
const char* cell_as_sgr(const GPUCell *, const GPUCell *);
|
||||
static inline bool cell_has_text(const CPUCell *c) { return c->ch_is_idx || c->ch_or_idx; }
|
||||
static inline void cell_set_char(CPUCell *c, char_type ch) { c->ch_is_idx = false; c->ch_or_idx = ch; }
|
||||
static inline bool cell_is_char(const CPUCell *c, char_type ch) { return !c->ch_is_idx && c->ch_or_idx == ch; }
|
||||
static inline unsigned num_codepoints_in_cell(const CPUCell *c, const TextCache *tc) {
|
||||
return c->ch_is_idx ? tc_num_codepoints(tc, c->ch_or_idx) : (c->ch_or_idx ? 1 : 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
text_in_cell(const CPUCell *c, const TextCache *tc, ListOfChars *ans) {
|
||||
if (c->ch_is_idx) tc_chars_at_index(tc, c->ch_or_idx, ans);
|
||||
else {
|
||||
ans->count = 1;
|
||||
ans->chars[0] = c->ch_or_idx;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
cell_set_chars(CPUCell *c, TextCache *tc, const ListOfChars *lc) {
|
||||
if (lc->count <= 1) cell_set_char(c, lc->chars[0]);
|
||||
else {
|
||||
c->ch_or_idx = tc_get_or_insert_chars(tc, lc);
|
||||
c->ch_is_idx = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline char_type
|
||||
cell_first_char(const CPUCell *c, const TextCache *tc) {
|
||||
if (c->ch_is_idx) return tc_first_char_at_index(tc, c->ch_or_idx);
|
||||
return c->ch_or_idx;
|
||||
}
|
||||
|
||||
|
||||
static inline CellAttrs
|
||||
cursor_to_attrs(const Cursor *c, const uint16_t width) {
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ 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) {
|
||||
const CellAttrs empty = {.width=1};
|
||||
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; cpu_cells[i].hyperlink_id = 0; gpu_cells[i].attrs = empty; }
|
||||
static const CellAttrs empty = {.width=1};
|
||||
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].val = 0; cpu_cells[i].ch_or_idx = ch; gpu_cells[i].attrs = empty; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,8 @@ static inline index_type
|
|||
xlimit_for_line(const Line *line) {
|
||||
index_type xlimit = line->xnum;
|
||||
if (BLANK_CHAR == 0) {
|
||||
while (xlimit > 0 && (line->cpu_cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--;
|
||||
const CPUCell *c;
|
||||
while (xlimit > 0 && !((c = line->cpu_cells + xlimit - 1)->ch_is_idx) && c->ch_or_idx == BLANK_CHAR) xlimit--;
|
||||
if (xlimit < line->xnum && line->gpu_cells[xlimit > 0 ? xlimit - 1 : xlimit].attrs.width == 2) xlimit++;
|
||||
}
|
||||
return xlimit;
|
||||
|
|
@ -67,8 +68,7 @@ left_shift_line(Line *line, index_type at, index_type num) {
|
|||
const CellAttrs empty = {.width=1};
|
||||
const CellAttrs zero = {{0}};
|
||||
if (at < line->xnum && line->gpu_cells[at].attrs.width != 1) {
|
||||
line->cpu_cells[at].ch = BLANK_CHAR;
|
||||
line->cpu_cells[at].hyperlink_id = 0;
|
||||
line->cpu_cells[at].val = 0;
|
||||
line->gpu_cells[at].attrs = BLANK_CHAR ? empty : zero;
|
||||
clear_sprite_position(line->gpu_cells[at]);
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ left_shift_line(Line *line, index_type at, index_type num) {
|
|||
static inline bool
|
||||
line_is_empty(const Line *line) {
|
||||
for (index_type i = 0; i < line->xnum; i++) {
|
||||
if (line->cpu_cells[i].ch != BLANK_CHAR) return false;
|
||||
if (line->cpu_cells[i].ch_is_idx || line->cpu_cells[i].ch_or_idx != BLANK_CHAR) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -88,16 +88,14 @@ void line_apply_cursor(Line *self, const Cursor *cursor, unsigned int at, unsign
|
|||
char_type line_get_char(Line *self, index_type at);
|
||||
void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *, hyperlink_id_type);
|
||||
void line_right_shift(Line *, unsigned int , unsigned int );
|
||||
void line_add_combining_char(CPUCell *, GPUCell *, uint32_t , unsigned int );
|
||||
bool line_add_combining_char(CPUCell *, GPUCell *, TextCache*, ListOfChars*, uint32_t , unsigned int );
|
||||
index_type line_url_start_at(Line *self, index_type x);
|
||||
index_type line_url_end_at(Line *self, index_type x, bool, char_type, bool, bool, index_type);
|
||||
bool line_startswith_url_chars(Line*, bool);
|
||||
bool line_as_ansi(Line *self, ANSIBuf *output, const GPUCell**, index_type start_at, index_type stop_before, char_type prefix_char) __attribute__((nonnull));
|
||||
unsigned int line_length(Line *self);
|
||||
size_t cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
||||
size_t cell_as_unicode_for_fallback(CPUCell *cell, Py_UCS4 *buf);
|
||||
size_t cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type);
|
||||
size_t cell_as_utf8_for_fallback(CPUCell *cell, char *buf);
|
||||
size_t cell_as_unicode_for_fallback(const ListOfChars *lc, Py_UCS4 *buf);
|
||||
size_t cell_as_utf8_for_fallback(CPUCell *cell, TextCache *tc, char *buf);
|
||||
PyObject* unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells);
|
||||
PyObject* line_as_unicode(Line *, bool);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf
|
|||
src_x_limit = src->xnum;
|
||||
if (!src_line_is_continued) {
|
||||
// Trim trailing blanks since there is a hard line break at the end of this line
|
||||
while(src_x_limit && (src->line->cpu_cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--;
|
||||
while(src_x_limit && src->line->cpu_cells[src_x_limit - 1].ch_or_idx == BLANK_CHAR && !src->line->cpu_cells[src_x_limit - 1].ch_is_idx) src_x_limit--;
|
||||
} else {
|
||||
src->line->gpu_cells[src->xnum-1].attrs.next_char_was_wrapped = false;
|
||||
}
|
||||
|
|
|
|||
100
kitty/screen.c
100
kitty/screen.c
|
|
@ -136,10 +136,11 @@ new_screen_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||
self->grman = self->main_grman;
|
||||
self->disable_ligatures = OPT(disable_ligatures);
|
||||
self->main_tabstops = PyMem_Calloc(2 * self->columns, sizeof(bool));
|
||||
self->lc = alloc_list_of_chars();
|
||||
if (
|
||||
self->cursor == NULL || self->main_linebuf == NULL || self->alt_linebuf == NULL ||
|
||||
self->main_tabstops == NULL || self->historybuf == NULL || self->main_grman == NULL ||
|
||||
self->alt_grman == NULL || self->color_profile == NULL
|
||||
self->alt_grman == NULL || self->color_profile == NULL || self->lc == NULL
|
||||
) {
|
||||
Py_CLEAR(self); return NULL;
|
||||
}
|
||||
|
|
@ -335,7 +336,7 @@ found:
|
|||
linebuf_init_line(self->main_linebuf, y);
|
||||
// this is needed because screen_resize() checks to see if the cursor is beyond the content,
|
||||
// so insert some fake content
|
||||
self->main_linebuf->line->cpu_cells[0].ch = ' ';
|
||||
cell_set_char(self->main_linebuf->line->cpu_cells, ' ');
|
||||
if (y < (int)self->cursor->y) (*num_of_prompt_lines_above_cursor)++;
|
||||
}
|
||||
}
|
||||
|
|
@ -346,10 +347,10 @@ static bool
|
|||
preserve_blank_output_start_line(Cursor *cursor, LineBuf *linebuf) {
|
||||
if (cursor->x == 0 && cursor->y < linebuf->ynum && !linebuf->line_attrs[cursor->y].is_continued) {
|
||||
linebuf_init_line(linebuf, cursor->y);
|
||||
if (!linebuf->line->cpu_cells[0].ch) {
|
||||
if (!cell_has_text(linebuf->line->cpu_cells)) {
|
||||
// we have a blank output start line, we need it to be preserved by
|
||||
// reflow, so insert a dummy char
|
||||
linebuf->line->cpu_cells[cursor->x++].ch = '<';
|
||||
cell_set_char(linebuf->line->cpu_cells + cursor->x++, '<');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -360,7 +361,7 @@ static void
|
|||
remove_blank_output_line_reservation_marker(Cursor *cursor, LineBuf *linebuf) {
|
||||
if (cursor->y < linebuf->ynum) {
|
||||
linebuf_init_line(linebuf, cursor->y);
|
||||
linebuf->line->cpu_cells[0].ch = 0;
|
||||
cell_set_char(linebuf->line->cpu_cells, 0);
|
||||
cursor->x = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -529,6 +530,7 @@ dealloc(Screen* self) {
|
|||
free_hyperlink_pool(self->hyperlink_pool);
|
||||
free(self->as_ansi_buf.buf);
|
||||
free(self->last_rendered_window_char.canvas);
|
||||
if (self->lc) { cleanup_list_of_chars(self->lc); free(self->lc); self->lc = NULL; }
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
} // }}}
|
||||
|
||||
|
|
@ -643,8 +645,9 @@ draw_second_flag_codepoint(Screen *self, char_type ch) {
|
|||
CPUCell *cp; GPUCell *gp;
|
||||
linebuf_init_cells(self->linebuf, ypos, &cp, &gp);
|
||||
CPUCell *cell = cp + xpos;
|
||||
if (!is_flag_pair(cell->ch, ch) || cell->cc_idx[0]) return false;
|
||||
line_add_combining_char(cp, gp, ch, xpos);
|
||||
text_in_cell(cell, self->text_cache, self->lc);
|
||||
if (self->lc->count != 1 || !is_flag_pair(self->lc->chars[0], ch)) return false;
|
||||
line_add_combining_char(cp, gp, self->text_cache, self->lc, ch, xpos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -670,11 +673,12 @@ draw_combining_char(Screen *self, text_loop_state *s, char_type ch) {
|
|||
if (has_prev_char) {
|
||||
CPUCell *cp; GPUCell *gp;
|
||||
linebuf_init_cells(self->linebuf, ypos, &cp, &gp);
|
||||
line_add_combining_char(cp, gp, ch, xpos);
|
||||
if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji
|
||||
bool added = line_add_combining_char(cp, gp, self->text_cache, self->lc, ch, xpos);
|
||||
unsigned base_pos = self->lc->count - (added ? 2 : 1);
|
||||
if (ch == VS16) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji
|
||||
CPUCell *cpu_cell = cp + xpos;
|
||||
GPUCell *gpu_cell = gp + xpos;
|
||||
if (gpu_cell->attrs.width != 2 && cpu_cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cpu_cell->ch)) {
|
||||
if (gpu_cell->attrs.width != 2 && added && self->lc->chars[base_pos + 1] == VS16 && is_emoji_presentation_base(self->lc->chars[base_pos])) {
|
||||
gpu_cell->attrs.width = 2;
|
||||
if (xpos + 1 < self->columns) {
|
||||
zero_cells(s, cp + xpos + 1, gp + xpos + 1);
|
||||
|
|
@ -682,13 +686,13 @@ draw_combining_char(Screen *self, text_loop_state *s, char_type ch) {
|
|||
self->cursor->x++;
|
||||
} else move_widened_char(self, s, cpu_cell, gpu_cell, xpos, ypos);
|
||||
}
|
||||
} else if (ch == 0xfe0e) {
|
||||
} else if (ch == VS15) {
|
||||
CPUCell *cpu_cell = cp + xpos;
|
||||
GPUCell *gpu_cell = gp + xpos;
|
||||
if (gpu_cell->attrs.width == 0 && cpu_cell->ch == 0 && xpos > 0) {
|
||||
if (gpu_cell->attrs.width == 0 && !cell_has_text(cpu_cell) && xpos > 0) {
|
||||
cpu_cell--; gpu_cell--;
|
||||
}
|
||||
if (gpu_cell->attrs.width == 2 && cpu_cell->cc_idx[0] == VS15 && is_emoji_presentation_base(cpu_cell->ch)) {
|
||||
if (gpu_cell->attrs.width == 2 && added && self->lc->chars[base_pos + 1] == VS15 && is_emoji_presentation_base(self->lc->chars[base_pos])) {
|
||||
gpu_cell->attrs.width = 1;
|
||||
self->cursor->x--;
|
||||
}
|
||||
|
|
@ -716,7 +720,7 @@ cursor_on_wide_char_trailer(Screen *self, text_loop_state *s) {
|
|||
static void
|
||||
move_cursor_off_wide_char_trailer(Screen *self, text_loop_state *s) {
|
||||
zero_cells(s, s->cp + self->cursor->x - 1, s->gp + self->cursor->x - 1);
|
||||
s->cp[self->cursor->x-1].ch = ' ';
|
||||
cell_set_char(&s->cp[self->cursor->x-1], ' ');
|
||||
zero_cells(s, s->cp + self->cursor->x, s->gp + self->cursor->x);
|
||||
}
|
||||
|
||||
|
|
@ -828,7 +832,7 @@ draw_text_loop(Screen *self, const uint32_t *chars, size_t num_chars, text_loop_
|
|||
s->image_placeholder_marked = true;
|
||||
}
|
||||
zero_cells(s, s->cp + self->cursor->x, s->gp + self->cursor->x);
|
||||
s->cp[self->cursor->x].ch = ch;
|
||||
cell_set_char(&s->cp[self->cursor->x], ch);
|
||||
self->cursor->x++;
|
||||
if (char_width == 2) {
|
||||
s->gp[self->cursor->x-1].attrs.width = 2;
|
||||
|
|
@ -1449,15 +1453,15 @@ screen_tab(Screen *self) {
|
|||
bool ok = true;
|
||||
for (combining_type i = 0; i < diff; i++) {
|
||||
CPUCell *c = cpu_cell + i;
|
||||
if (c->ch != ' ' && c->ch != 0) { ok = false; break; }
|
||||
if (cell_has_text(c) && !cell_is_char(c, ' ')) { ok = false; break; }
|
||||
}
|
||||
if (ok) {
|
||||
for (combining_type i = 0; i < diff; i++) {
|
||||
CPUCell *c = cpu_cell + i;
|
||||
c->ch = ' '; zero_at_ptr_count(c->cc_idx, arraysz(c->cc_idx));
|
||||
cell_set_char(c, ' ');
|
||||
}
|
||||
cpu_cell->ch = '\t';
|
||||
cpu_cell->cc_idx[0] = diff;
|
||||
self->lc->count = 2; self->lc->chars[0] = '\t'; self->lc->chars[1] = diff;
|
||||
cell_set_chars(cpu_cell, self->text_cache, self->lc);
|
||||
}
|
||||
}
|
||||
self->cursor->x = found;
|
||||
|
|
@ -2618,8 +2622,7 @@ screen_has_marker(Screen *self) {
|
|||
return self->marker != NULL;
|
||||
}
|
||||
|
||||
static uint32_t diacritic_to_rowcolumn(combining_type m) {
|
||||
char_type c = codepoint_for_mark(m);
|
||||
static uint32_t diacritic_to_rowcolumn(char_type c) {
|
||||
return diacritic_to_num(c);
|
||||
}
|
||||
|
||||
|
|
@ -2656,21 +2659,22 @@ screen_render_line_graphics(Screen *self, Line *line, int32_t row) {
|
|||
uint32_t cur_img_id_higher8bits = 0;
|
||||
uint32_t cur_img_row = 0;
|
||||
uint32_t cur_img_col = 0;
|
||||
if (cpu_cell->ch == IMAGE_PLACEHOLDER_CHAR) {
|
||||
if (cell_first_char(cpu_cell, self->text_cache) == IMAGE_PLACEHOLDER_CHAR) {
|
||||
line->attrs.has_image_placeholders = true;
|
||||
// The lower 24 bits of the image id are encoded in the foreground
|
||||
// color, and the placement id is (optionally) in the underline color.
|
||||
cur_img_id_lower24bits = color_to_id(gpu_cell->fg);
|
||||
cur_placement_id = color_to_id(gpu_cell->decoration_fg);
|
||||
text_in_cell(cpu_cell, self->text_cache, self->lc);
|
||||
// If the char has diacritics, use them as row and column indices.
|
||||
if (cpu_cell->cc_idx[0])
|
||||
cur_img_row = diacritic_to_rowcolumn(cpu_cell->cc_idx[0]);
|
||||
if (cpu_cell->cc_idx[1])
|
||||
cur_img_col = diacritic_to_rowcolumn(cpu_cell->cc_idx[1]);
|
||||
if (self->lc->count > 1 && self->lc->chars[1])
|
||||
cur_img_row = diacritic_to_rowcolumn(self->lc->chars[1]);
|
||||
if (self->lc->count > 2 && self->lc->chars[2])
|
||||
cur_img_col = diacritic_to_rowcolumn(self->lc->chars[2]);
|
||||
// The third diacritic is used to encode the higher 8 bits of the
|
||||
// image id (optional).
|
||||
if (cpu_cell->cc_idx[2])
|
||||
cur_img_id_higher8bits = diacritic_to_rowcolumn(cpu_cell->cc_idx[2]);
|
||||
if (self->lc->count > 3 && self->lc->chars[3])
|
||||
cur_img_id_higher8bits = diacritic_to_rowcolumn(self->lc->chars[3]);
|
||||
}
|
||||
// The current run is continued if the lower 24 bits of the image id and
|
||||
// the placement id are the same as in the previous cell and everything
|
||||
|
|
@ -2697,7 +2701,7 @@ screen_render_line_graphics(Screen *self, Line *line, int32_t row) {
|
|||
prev_img_row - 1, run_length, 1, self->cell_size);
|
||||
}
|
||||
// Start a new run.
|
||||
if (cpu_cell->ch == IMAGE_PLACEHOLDER_CHAR) {
|
||||
if (cell_first_char(cpu_cell, self->text_cache) == IMAGE_PLACEHOLDER_CHAR) {
|
||||
run_length = 1;
|
||||
if (!cur_img_col) cur_img_col = 1;
|
||||
if (!cur_img_row) cur_img_row = 1;
|
||||
|
|
@ -2755,7 +2759,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
|||
for (index_type y = 0; y < self->lines; y++) {
|
||||
linebuf_init_line(linebuf, y);
|
||||
if (linebuf->line->attrs.has_dirty_text) {
|
||||
render_line(fonts_data, linebuf->line, y, &self->paused_rendering.cursor, self->disable_ligatures);
|
||||
render_line(fonts_data, linebuf->line, y, &self->paused_rendering.cursor, self->disable_ligatures, self->lc);
|
||||
screen_render_line_graphics(self, linebuf->line, y);
|
||||
if (linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, linebuf->line);
|
||||
linebuf_mark_line_clean(linebuf, y);
|
||||
|
|
@ -2780,7 +2784,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
|||
// the unicode placeholder was first scanned can alter it.
|
||||
screen_render_line_graphics(self, self->historybuf->line, y - self->scrolled_by);
|
||||
if (self->historybuf->line->attrs.has_dirty_text) {
|
||||
render_line(fonts_data, self->historybuf->line, lnum, self->cursor, self->disable_ligatures);
|
||||
render_line(fonts_data, self->historybuf->line, lnum, self->cursor, self->disable_ligatures, self->lc);
|
||||
if (screen_has_marker(self)) mark_text_in_line(self->marker, self->historybuf->line);
|
||||
historybuf_mark_line_clean(self->historybuf, lnum);
|
||||
}
|
||||
|
|
@ -2791,7 +2795,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat
|
|||
linebuf_init_line(self->linebuf, lnum);
|
||||
if (self->linebuf->line->attrs.has_dirty_text ||
|
||||
(cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor_y == lnum))) {
|
||||
render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures);
|
||||
render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures, self->lc);
|
||||
screen_render_line_graphics(self, self->linebuf->line, y - self->scrolled_by);
|
||||
if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(self->marker, self->linebuf->line);
|
||||
if (is_overlay_active && lnum == self->overlay_line.ynum) render_overlay_line(self, self->linebuf->line, fonts_data);
|
||||
|
|
@ -3032,9 +3036,9 @@ limit_without_trailing_whitespace(const Line *line, index_type limit) {
|
|||
if (!limit) return limit;
|
||||
if (limit > line->xnum) limit = line->xnum;
|
||||
while (limit > 0) {
|
||||
CPUCell *cell = line->cpu_cells + limit - 1;
|
||||
if (cell->cc_idx[0]) break;
|
||||
switch(cell->ch) {
|
||||
const CPUCell *cell = line->cpu_cells + limit - 1;
|
||||
if (cell->ch_is_idx) break;
|
||||
switch(cell->ch_or_idx) {
|
||||
case ' ': case '\t': case '\n': case '\r': case 0: break;
|
||||
default:
|
||||
return limit;
|
||||
|
|
@ -3216,13 +3220,13 @@ extend_url(Screen *screen, Line *line, index_type *x, index_type *y, char_type s
|
|||
next_line_starts_with_url_chars = line_startswith_url_chars(line, in_hostname);
|
||||
has_newline = !line->attrs.is_continued;
|
||||
if (next_line_starts_with_url_chars && has_newline && !newlines_allowed) next_line_starts_with_url_chars = false;
|
||||
if (sentinel && next_line_starts_with_url_chars && line->cpu_cells[0].ch == sentinel) next_line_starts_with_url_chars = false;
|
||||
if (sentinel && next_line_starts_with_url_chars && cell_is_char(line->cpu_cells, sentinel)) next_line_starts_with_url_chars = false;
|
||||
}
|
||||
line = screen_visual_line(screen, *y + 1);
|
||||
if (!line) break;
|
||||
if (in_hostname) {
|
||||
for (last_hostname_char_pos = 0; last_hostname_char_pos < line->xnum; last_hostname_char_pos++) {
|
||||
if (line->cpu_cells[last_hostname_char_pos].ch == '/') {
|
||||
if (cell_is_char(line->cpu_cells + last_hostname_char_pos, '/')) {
|
||||
if (last_hostname_char_pos > 0) last_hostname_char_pos--;
|
||||
else { in_hostname = false; last_hostname_char_pos = line->xnum; }
|
||||
break;
|
||||
|
|
@ -3235,7 +3239,7 @@ extend_url(Screen *screen, Line *line, index_type *x, index_type *y, char_type s
|
|||
}
|
||||
if (sentinel && *x == 0 && *y > orig_y) {
|
||||
line = screen_visual_line(screen, *y);
|
||||
if (line && line->cpu_cells[0].ch == sentinel) {
|
||||
if (line && cell_is_char(line->cpu_cells, sentinel)) {
|
||||
*y -= 1; *x = line->xnum - 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -3244,7 +3248,7 @@ extend_url(Screen *screen, Line *line, index_type *x, index_type *y, char_type s
|
|||
static char_type
|
||||
get_url_sentinel(Line *line, index_type url_start) {
|
||||
char_type before = 0, sentinel;
|
||||
if (url_start > 0 && url_start < line->xnum) before = line->cpu_cells[url_start - 1].ch;
|
||||
if (url_start > 0 && url_start < line->xnum) before = cell_first_char(line->cpu_cells + url_start - 1, line->text_cache);
|
||||
switch(before) {
|
||||
case '"':
|
||||
case '\'':
|
||||
|
|
@ -3291,8 +3295,7 @@ screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
|||
sentinel = get_url_sentinel(line, url_start);
|
||||
index_type slash_count = 0;
|
||||
for (index_type i = url_start; i < line->xnum; i++) {
|
||||
const char_type ch = line->cpu_cells[i].ch;
|
||||
if (ch == '/' && ++slash_count > 2) { last_hostname_char_pos = i - 1; break; }
|
||||
if (cell_is_char(line->cpu_cells + i, '/') && ++slash_count > 2) { last_hostname_char_pos = i - 1; break; }
|
||||
}
|
||||
url_end = line_url_end_at(line, x, true, sentinel, next_line_starts_with_url_chars, x <= last_hostname_char_pos, last_hostname_char_pos);
|
||||
}
|
||||
|
|
@ -3442,7 +3445,7 @@ render_overlay_line(Screen *self, Line *line, FONTS_DATA_HANDLE fonts_data) {
|
|||
#define ol self->overlay_line
|
||||
line_save_cells(line, 0, line->xnum, ol.original_line.gpu_cells, ol.original_line.cpu_cells);
|
||||
screen_draw_overlay_line(self);
|
||||
render_line(fonts_data, line, ol.ynum, self->cursor, self->disable_ligatures);
|
||||
render_line(fonts_data, line, ol.ynum, self->cursor, self->disable_ligatures, self->lc);
|
||||
line_save_cells(line, 0, line->xnum, ol.gpu_cells, ol.cpu_cells);
|
||||
line_reset_cells(line, 0, line->xnum, ol.original_line.gpu_cells, ol.original_line.cpu_cells);
|
||||
ol.is_dirty = false;
|
||||
|
|
@ -4028,14 +4031,18 @@ text_for_marked_url(Screen *self, PyObject *args) {
|
|||
return text_for_selections(self, &self->url_ranges, ansi, strip_trailing_whitespace);
|
||||
}
|
||||
|
||||
static bool
|
||||
cell_is_blank(const CPUCell *c) {
|
||||
return !cell_has_text(c) || cell_is_char(c, ' ');
|
||||
}
|
||||
|
||||
bool
|
||||
screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end) {
|
||||
if (y >= self->lines) { return false; }
|
||||
Line *line = visual_line_(self, y);
|
||||
index_type xlimit = line->xnum, xstart = 0;
|
||||
while (xlimit > 0 && CHAR_IS_BLANK(line->cpu_cells[xlimit - 1].ch)) xlimit--;
|
||||
while (xstart < xlimit && CHAR_IS_BLANK(line->cpu_cells[xstart].ch)) xstart++;
|
||||
while (xlimit > 0 && cell_is_blank(line->cpu_cells + xlimit - 1)) xlimit--;
|
||||
while (xstart < xlimit && cell_is_blank(line->cpu_cells + xstart)) xstart++;
|
||||
*start = xstart; *end = xlimit > 0 ? xlimit - 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -4060,11 +4067,10 @@ is_opt_word_char(char_type ch, bool forward) {
|
|||
|
||||
static bool
|
||||
is_char_ok_for_word_extension(Line* line, index_type x, bool forward) {
|
||||
char_type ch = line->cpu_cells[x].ch;
|
||||
char_type ch = cell_first_char(line->cpu_cells + x, line->text_cache);
|
||||
if (is_word_char(ch) || is_opt_word_char(ch, forward)) return true;
|
||||
// pass : from :// so that common URLs are matched
|
||||
if (ch == ':' && x + 2 < line->xnum && line->cpu_cells[x+1].ch == '/' && line->cpu_cells[x+2].ch == '/') return true;
|
||||
return false;
|
||||
return ch == ':' && x + 2 < line->xnum && cell_is_char(line->cpu_cells + x + 1, '/') && cell_is_char(line->cpu_cells + x + 2, '/');
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ typedef struct {
|
|||
Selections selections, url_ranges;
|
||||
} paused_rendering;
|
||||
CharsetState charset;
|
||||
ListOfChars *lc;
|
||||
} Screen;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ typedef struct Chars {
|
|||
|
||||
#define NAME chars_map
|
||||
#define KEY_TY Chars
|
||||
#define VAL_TY CharOrIndex
|
||||
#define VAL_TY char_type
|
||||
static uint64_t hash_chars(Chars k);
|
||||
static bool cmpr_chars(Chars a, Chars b);
|
||||
#define HASH_FN hash_chars
|
||||
|
|
@ -21,7 +21,7 @@ static bool cmpr_chars(Chars a, Chars b);
|
|||
#include "kitty-verstable.h"
|
||||
|
||||
typedef struct TextCache {
|
||||
struct { Chars *items; size_t count, capacity; } array;
|
||||
struct { Chars *items; size_t capacity; char_type count; } array;
|
||||
chars_map map;
|
||||
unsigned refcnt;
|
||||
} TextCache;
|
||||
|
|
@ -52,7 +52,7 @@ tc_clear(TextCache *ans) {
|
|||
static void
|
||||
free_text_cache(TextCache *self) {
|
||||
vt_cleanup(&self->map);
|
||||
for (size_t i = 0; i < self->array.count; i++) free((char_type*)self->array.items[i].chars);
|
||||
for (char_type i = 0; i < self->array.count; i++) free((char_type*)self->array.items[i].chars);
|
||||
free(self->array.items);
|
||||
free(self);
|
||||
}
|
||||
|
|
@ -69,32 +69,49 @@ tc_decref(TextCache *self) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
char_type
|
||||
tc_first_char_at_index(const TextCache *self, char_type idx) {
|
||||
if (self->array.count > idx) return self->array.items[idx].chars[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tc_chars_at_index(const TextCache *self, CharOrIndex idx, ListOfChars *ans) {
|
||||
if (idx.ch_is_index) {
|
||||
if (self->array.count > idx.ch) {
|
||||
ans->count = self->array.items[idx.ch].count;
|
||||
ensure_space_for(ans, chars, char_type, ans->count, capacity, 8, false);
|
||||
memcpy(ans->chars, self->array.items[idx.ch].chars, sizeof(ans->chars[0]) * ans->count);
|
||||
} else {
|
||||
ans->count = 0;
|
||||
}
|
||||
tc_chars_at_index(const TextCache *self, char_type idx, ListOfChars *ans) {
|
||||
if (self->array.count > idx) {
|
||||
ans->count = self->array.items[idx].count;
|
||||
ensure_space_for_chars(ans, ans->count);
|
||||
memcpy(ans->chars, self->array.items[idx].chars, sizeof(ans->chars[0]) * ans->count);
|
||||
} else {
|
||||
ans->count = 1;
|
||||
ensure_space_for(ans, chars, char_type, 1, capacity, 8, false);
|
||||
ans->chars[0] = idx.ch;
|
||||
ans->count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static CharOrIndex
|
||||
unsigned
|
||||
tc_num_codepoints(const TextCache *self, char_type idx) {
|
||||
return self->array.count > idx ? self->array.items[idx].count : 0;
|
||||
}
|
||||
|
||||
unsigned
|
||||
tc_chars_at_index_ansi(const TextCache *self, char_type idx, ANSIBuf *output) {
|
||||
unsigned count = 0;
|
||||
if (self->array.count > idx) {
|
||||
count = self->array.items[idx].count;
|
||||
ensure_space_for(output, buf, output->buf[0], output->len + count, capacity, 2048, false);
|
||||
memcpy(output->buf + output->len, self->array.items[idx].chars, sizeof(output->buf[0]) * count);
|
||||
output->len += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static char_type
|
||||
copy_and_insert(TextCache *self, const Chars key) {
|
||||
if (self->array.count >= (1llu << (8*sizeof(char_type) - 1)) - 1) fatal("Too many items in TextCache");
|
||||
if (self->array.count > MAX_CHAR_TYPE_VALUE) fatal("Too many items in TextCache");
|
||||
ensure_space_for(&(self->array), items, Chars, self->array.count + 1, capacity, 256, false);
|
||||
char_type *copy = malloc(key.count * sizeof(key.chars[0]));
|
||||
if (!copy) fatal("Out of memory");
|
||||
memcpy(copy, key.chars, key.count * sizeof(key.chars[0]));
|
||||
CharOrIndex ans;
|
||||
ans.ch_is_index = 1; ans.ch = self->array.count;
|
||||
char_type ans = self->array.count;
|
||||
Chars *k = self->array.items + self->array.count++;
|
||||
k->count = key.count; k->chars = copy;
|
||||
chars_map_itr i = vt_insert(&self->map, *k, ans);
|
||||
|
|
@ -102,13 +119,8 @@ copy_and_insert(TextCache *self, const Chars key) {
|
|||
return ans;
|
||||
}
|
||||
|
||||
CharOrIndex
|
||||
char_type
|
||||
tc_get_or_insert_chars(TextCache *self, const ListOfChars *chars) {
|
||||
if (chars->count == 1) {
|
||||
CharOrIndex ans = {0};
|
||||
ans.ch = chars->chars[0];
|
||||
return ans;
|
||||
}
|
||||
Chars key = {.count=chars->count, .chars=chars->chars};
|
||||
chars_map_itr i = vt_get(&self->map, key);
|
||||
if (vt_is_end(i)) return copy_and_insert(self, key);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,33 @@ typedef struct ListOfChars {
|
|||
size_t count, capacity;
|
||||
} ListOfChars;
|
||||
|
||||
#define LIST_OF_CHARS_STACK_SIZE 4
|
||||
static inline void cleanup_list_of_chars(ListOfChars *lc) { if (lc->capacity > LIST_OF_CHARS_STACK_SIZE) free(lc->chars); }
|
||||
#define RAII_ListOfChars(name) char_type name##lcbuf[LIST_OF_CHARS_STACK_SIZE]; __attribute__((cleanup(cleanup_list_of_chars))) ListOfChars name = {.chars=name##lcbuf, .capacity = LIST_OF_CHARS_STACK_SIZE};
|
||||
static inline ListOfChars* alloc_list_of_chars(void) {
|
||||
ListOfChars *ans = calloc(1, sizeof(ListOfChars));
|
||||
if (ans) {
|
||||
ans->capacity = LIST_OF_CHARS_STACK_SIZE * 2;
|
||||
ans->chars = malloc(ans->capacity * sizeof(ans->chars[0]));
|
||||
if (!ans->chars) { free(ans); ans = NULL; }
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ensure_space_for_chars(ListOfChars *lc, size_t count) {
|
||||
if (lc->capacity >= count) return;
|
||||
if (lc->capacity > LIST_OF_CHARS_STACK_SIZE) {
|
||||
ensure_space_for(lc, chars, char_type, count, capacity, count, false);
|
||||
} else {
|
||||
lc->capacity = count + LIST_OF_CHARS_STACK_SIZE;
|
||||
void *chars = malloc(lc->capacity * sizeof(lc->chars[0]));
|
||||
if (!chars) fatal("Out of memory allocating LCChars char space");
|
||||
memcpy(chars, lc->chars, lc->count * sizeof(lc->chars[0]));
|
||||
lc->chars = chars;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef TEXT_CACHE_IMPLEMENTATION
|
||||
typedef struct {int x; } *TextCache;
|
||||
#endif
|
||||
|
|
@ -22,5 +49,8 @@ TextCache* tc_alloc(void);
|
|||
TextCache* tc_incref(TextCache *self);
|
||||
TextCache* tc_decref(TextCache *self);
|
||||
void tc_clear(TextCache *ans);
|
||||
void tc_chars_at_index(const TextCache *self, CharOrIndex idx, ListOfChars *ans);
|
||||
CharOrIndex tc_get_or_insert_chars(TextCache *self, const ListOfChars *chars);
|
||||
void tc_chars_at_index(const TextCache *self, char_type idx, ListOfChars *ans);
|
||||
unsigned tc_chars_at_index_ansi(const TextCache *self, char_type idx, ANSIBuf *output);
|
||||
char_type tc_get_or_insert_chars(TextCache *self, const ListOfChars *chars);
|
||||
char_type tc_first_char_at_index(const TextCache *self, char_type idx);
|
||||
unsigned tc_num_codepoints(const TextCache *self, char_type idx);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
#pragma once
|
||||
#include "data-types.h"
|
||||
#include "state.h"
|
||||
// START_KNOWN_MARKS
|
||||
static const combining_type VS15 = 1364, VS16 = 1365;
|
||||
// END_KNOWN_MARKS
|
||||
|
||||
// Converts row/column diacritics to numbers.
|
||||
int diacritic_to_num(char_type ch);
|
||||
|
|
@ -14,8 +11,6 @@ bool is_word_char(char_type ch);
|
|||
bool is_CZ_category(char_type);
|
||||
bool is_P_category(char_type);
|
||||
bool is_non_rendered_char(char_type);
|
||||
char_type codepoint_for_mark(combining_type m);
|
||||
combining_type mark_for_codepoint(char_type c);
|
||||
|
||||
static inline bool
|
||||
is_excluded_from_url(uint32_t ch) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue