Replace ctypes in marks.py with C-level set_uint_at_address function

This commit is contained in:
copilot-swe-agent[bot] 2026-06-25 10:35:11 +00:00 committed by GitHub
parent 9b2c7ddcf5
commit c35ff2e843
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 67 additions and 23 deletions

View file

@ -683,6 +683,17 @@ py_get_config_dir(PyObject *self UNUSED, PyObject *args UNUSED) {
#include "launcher/cli-parser.h"
static PyObject*
set_uint_at_address(PyObject *self UNUSED, PyObject *args) {
PyObject *address_obj;
unsigned int value;
if (!PyArg_ParseTuple(args, "OI", &address_obj, &value)) return NULL;
void *ptr = PyLong_AsVoidPtr(address_obj);
if (ptr == NULL && PyErr_Occurred()) return NULL;
*((unsigned int*)ptr) = value;
Py_RETURN_NONE;
}
static PyMethodDef module_methods[] = {
METHODB(replace_c0_codes_except_nl_space_tab, METH_O),
METHODB(read_file, METH_O),
@ -716,6 +727,7 @@ static PyMethodDef module_methods[] = {
{"timed_debug_print", (PyCFunction)py_timed_debug_print, METH_VARARGS, ""},
{"find_in_memoryview", (PyCFunction)find_in_memoryview, METH_VARARGS, ""},
{"run_at_exit_cleanup_functions", (PyCFunction)py_run_atexit_cleanup_functions, METH_NOARGS, ""},
{"set_uint_at_address", (PyCFunction)set_uint_at_address, METH_VARARGS, ""},
#ifdef __APPLE__
METHODB(user_cache_dir, METH_NOARGS),
METHODB(process_group_map, METH_NOARGS),

View file

@ -3,26 +3,16 @@
import re
from collections.abc import Callable, Generator, Iterable, Sequence
from ctypes import POINTER, c_uint, c_void_p, cast
from re import Pattern
from typing import Union
from .fast_data_types import set_uint_at_address
from .utils import resolve_custom_file
pointer_to_uint = POINTER(c_uint)
MarkerFunc = Callable[[str, int, int, int], Generator[None, None, None]]
def get_output_variables(left_address: int, right_address: int, color_address: int) -> tuple[c_uint, c_uint, c_uint]:
return (
cast(c_void_p(left_address), pointer_to_uint).contents,
cast(c_void_p(right_address), pointer_to_uint).contents,
cast(c_void_p(color_address), pointer_to_uint).contents,
)
def marker_from_regex(expression: Union[str, 'Pattern[str]'], color: int, flags: int = re.UNICODE) -> MarkerFunc:
color = max(1, min(color, 3))
if isinstance(expression, str):
@ -31,11 +21,10 @@ def marker_from_regex(expression: Union[str, 'Pattern[str]'], color: int, flags:
pat = expression
def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
left, right, colorv = get_output_variables(left_address, right_address, color_address)
colorv.value = color
set_uint_at_address(color_address, color)
for match in pat.finditer(text):
left.value = match.start()
right.value = match.end() - 1
set_uint_at_address(left_address, match.start())
set_uint_at_address(right_address, match.end() - 1)
yield
return marker
@ -52,12 +41,11 @@ def marker_from_multiple_regex(regexes: Iterable[tuple[int, str]], flags: int =
pat = re.compile(expr, flags=flags)
def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
left, right, color = get_output_variables(left_address, right_address, color_address)
for match in pat.finditer(text):
left.value = match.start()
right.value = match.end() - 1
set_uint_at_address(left_address, match.start())
set_uint_at_address(right_address, match.end() - 1)
grp = match.lastgroup
color.value = color_map[grp] if grp is not None else 0
set_uint_at_address(color_address, color_map[grp] if grp is not None else 0)
yield
return marker
@ -69,11 +57,10 @@ def marker_from_text(expression: str, color: int) -> MarkerFunc:
def marker_from_function(func: Callable[[str], Iterable[tuple[int, int, int]]]) -> MarkerFunc:
def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:
left, right, colorv = get_output_variables(left_address, right_address, color_address)
for (ll, r, c) in func(text):
left.value = ll
right.value = r
colorv.value = c
set_uint_at_address(left_address, ll)
set_uint_at_address(right_address, r)
set_uint_at_address(color_address, c)
yield
return marker

View file

@ -940,3 +940,48 @@ class TestDataTypes(BaseTest):
s.reset()
s.draw('\0')
self.ae(str(s.line(0)), '')
def test_set_uint_at_address(self):
from ctypes import addressof, c_uint
from kitty.fast_data_types import set_uint_at_address
from kitty.marks import marker_from_function, marker_from_multiple_regex, marker_from_regex, marker_from_text
# Test set_uint_at_address directly
val = c_uint(0)
addr = addressof(val)
set_uint_at_address(addr, 42)
self.ae(val.value, 42)
set_uint_at_address(addr, 0)
self.ae(val.value, 0)
set_uint_at_address(addr, 0xFFFF)
self.ae(val.value, 0xFFFF)
# Test marker functions using set_uint_at_address via Screen
s = self.create_screen()
s.draw('abaa')
s.set_marker(marker_from_regex('a', 3))
self.ae(s.marked_cells(), [(0, 0, 3), (2, 0, 3), (3, 0, 3)])
s.set_marker()
self.ae(s.marked_cells(), [])
s = self.create_screen()
s.draw('aXbX')
s.set_marker(marker_from_multiple_regex([(1, 'a'), (2, 'X')]))
self.ae(s.marked_cells(), [(0, 0, 1), (1, 0, 2), (3, 0, 2)])
s = self.create_screen(cols=20)
s.draw('hello world')
s.set_marker(marker_from_text('world', 2))
self.ae(s.marked_cells(), [(6, 0, 2), (7, 0, 2), (8, 0, 2), (9, 0, 2), (10, 0, 2)])
def mark_func(text):
for i, ch in enumerate(text):
if ch == 'x':
yield i, i, 1
s = self.create_screen()
s.draw('axbxc')
s.set_marker(marker_from_function(mark_func))
self.ae(s.marked_cells(), [(1, 0, 1), (3, 0, 1)])