From 3e505885258c27393e39df1ba2215af49065b382 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 25 Mar 2025 16:52:01 +0530 Subject: [PATCH] Add a test for PUA recog --- gen/wcwidth.py | 2 +- kitty/char-props.h | 11 +++++++++-- kitty/data-types.c | 14 ++++++++++++++ kitty_tests/datatypes.py | 2 ++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/gen/wcwidth.py b/gen/wcwidth.py index 9a4d584a5..7deb463c1 100755 --- a/gen/wcwidth.py +++ b/gen/wcwidth.py @@ -717,7 +717,7 @@ def aw(s: Iterable[int], width: int) -> None: raw = f.read() nraw = re.sub(r'\d+/\*=width_shift\*/', f'{width_shift}/*=width_shift*/', raw) nraw = re.sub(r'// CharPropsDeclaration.+?// EndCharPropsDeclaration', CharProps.c_declaration(), nraw, flags=re.DOTALL) - nraw = re.sub(r'// UCBDeclaration.+?// EndUCBDeclaration', buf.getvalue(), nraw, flags=re.DOTALL) + nraw = re.sub(r'// UCBDeclaration.+?// EndUCBDeclaration', buf.getvalue().rstrip(), nraw, flags=re.DOTALL) if nraw != raw: f.seek(0) f.truncate() diff --git a/kitty/char-props.h b/kitty/char-props.h index 494fa635a..095d7668d 100644 --- a/kitty/char-props.h +++ b/kitty/char-props.h @@ -46,8 +46,6 @@ typedef enum UnicodeCategory { // EndUCBDeclaration - - // CharPropsDeclaration // Uses 23 bits typedef union CharProps { @@ -101,3 +99,12 @@ void grapheme_segmentation_reset(GraphemeSegmentationState *s); bool grapheme_segmentation_step(GraphemeSegmentationState *s, CharProps ch); static inline int wcwidth_std(CharProps ch) { return (int)ch.shifted_width - 4/*=width_shift*/; } static inline bool is_private_use(CharProps ch) { return ch.category == UC_Co; } +static inline const char* char_category(CharProps cp) { +#define a(x) case UC_##x: return #x + switch((UnicodeCategory)cp.category) { + a(Cn); a(Cc); a(Zs); a(Po); a(Sc); a(Ps); a(Pe); a(Sm); a(Pd); a(Nd); a(Lu); a(Sk); a(Pc); a(Ll); a(So); a(Lo); a(Pi); a(Cf); + a(No); a(Pf); a(Lt); a(Lm); a(Mn); a(Me); a(Mc); a(Nl); a(Zl); a(Zp); a(Cs); a(Co); + } + return "Cn"; +#undef a +} diff --git a/kitty/data-types.c b/kitty/data-types.c index 931d300d1..8b792bd11 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -632,6 +632,19 @@ py_run_atexit_cleanup_functions(PyObject *self UNUSED, PyObject *args UNUSED) { Py_RETURN_NONE; } +static PyObject* +py_char_props_for(PyObject *self UNUSED, PyObject *ch) { + if (!PyUnicode_Check(ch) || PyUnicode_GET_LENGTH(ch) != 1) { PyErr_SetString(PyExc_TypeError, "must suply a single character"); return NULL; } + char_type c = PyUnicode_READ_CHAR(ch, 0); + CharProps cp = char_props_for(c); +#define B(x) #x, cp.x ? Py_True : Py_False + return Py_BuildValue("{ si sO sB sB ss }", + "width", wcwidth_std(cp), B(is_extended_pictographic), "grapheme_break", cp.grapheme_break, + "indic_conjunct_break", cp.indic_conjunct_break, "category", char_category(cp) + ); +#undef B +} + static PyMethodDef module_methods[] = { METHODB(replace_c0_codes_except_nl_space_tab, METH_O), {"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""}, @@ -647,6 +660,7 @@ static PyMethodDef module_methods[] = { {"base64_encode_into", (PyCFunction)base64_encode_into, METH_VARARGS, ""}, {"base64_decode", (PyCFunction)(void (*) (void))(pybase64_decode), METH_O, ""}, {"base64_decode_into", (PyCFunction)base64_decode_into, METH_VARARGS, ""}, + {"char_props_for", py_char_props_for, METH_O, ""}, {"split_into_graphemes", (PyCFunction)split_into_graphemes, METH_O, ""}, {"thread_write", (PyCFunction)cm_thread_write, METH_VARARGS, ""}, {"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""}, diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index ab5033318..88700f3cc 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -11,6 +11,7 @@ Color, HistoryBuf, LineBuf, + char_props_for, expand_ansi_c_escapes, parse_input_from_terminal, replace_c0_codes_except_nl_space_tab, @@ -640,3 +641,4 @@ def test_split_into_graphemes(self): expected = test['data'] actual = split_into_graphemes(''.join(expected)) self.ae(expected, actual, f'Test #{i} failed: {test["comment"]}') + self.assertEqual(char_props_for('\ue000')['category'], 'Co')