Saltar a contenido

La frontera C

La carcasa y el núcleo se encuentran en una única cabecera C, textchum.h. Se genera a partir del código Rust con cbindgen en cada compilación del núcleo, y se versiona en el repositorio para que las herramientas del lado Swift funcionen sin una cadena de herramientas de Rust. La CI falla si una compilación deja la cabecera desactualizada, así que nunca puede divergir del código.

Convenciones

Cada función de la interfaz sigue el mismo pequeño conjunto de reglas.

Manejadores opacos. Los tipos del núcleo (TcApp, TcBuffer) son estructuras opacas; quien llama retiene punteros, los devuelve en cada llamada y los libera con la función tc_*_free correspondiente. Quien llama nunca reserva memoria en nombre del núcleo.

UTF-8 de entrada, longitudes explícitas. Las cadenas que entran al núcleo son pares (puntero, longitud) de bytes UTF-8 — sin terminadores nulos obligatorios, sin más codificación que UTF-8. Las cadenas que el núcleo devuelve son UTF-8 terminadas en nulo y de su propiedad; se liberan con tc_string_free.

Dos unidades de posición. Las funciones direccionan el texto en desplazamientos de bytes UTF-8 (la unidad nativa del núcleo) o en unidades UTF-16 (sufijo _utf16), porque es lo que cuentan NSRange y el Language Server Protocol. El núcleo hace la conversión; quien llama usa la unidad que tenga de forma natural.

Fallo transaccional. Las llamadas falibles devuelven bool. false significa que la entrada fue validada, rechazada y nada cambió — un desplazamiento fuera de rango, una posición en mitad de un carácter, UTF-8 inválido. Quien llama siempre puede tratar el fallo como «la operación no ocurrió».

Los pánicos no cruzan. Cada punto de entrada captura los pánicos de Rust y los convierte en el valor de fallo de la función. Un error del núcleo no puede desenrollarse hacia marcos de pila de Swift.

Un hilo de entrada, un hilo de salida. Las llamadas al núcleo deben venir de un solo hilo. Los eventos fluyen en sentido contrario por el callback registrado con tc_app_new, invocado en un único hilo de despacho propiedad del núcleo. La tarea del callback es trasladar el evento al hilo de interfaz de la carcasa; TextchumKit hace exactamente eso y nada más.

El canal de eventos

Cierta información se origina dentro del núcleo (hoy: respuestas pong usadas para verificar el canal; próximamente: diagnósticos de servidores de lenguaje, invalidaciones de coloreado). Llega a la carcasa como un TcEvent — un discriminante kind más la carga del evento — entregado al callback registrado.

Las carcasas deben tolerar valores de kind desconocidos: que un núcleo más nuevo emita un evento que una carcasa más vieja no entiende es compatibilidad hacia adelante, no un error.

tc_app_free bloquea hasta que los eventos en cola se hayan entregado y garantiza que el callback no se invoca nunca después, que es lo que hace que el desmontaje sea seguro de escribir del lado de la carcasa.

Superficie actual

Función Propósito
tc_version Versión del núcleo como cadena estática.
tc_app_new / tc_app_free Crear/destruir una instancia del núcleo y su canal de eventos.
tc_app_ping Pedir un pong asíncrono; ejercita la ruta de eventos.
tc_buffer_new / tc_buffer_free Crear/destruir un búfer de texto.
tc_buffer_insert Insertar UTF-8 en un desplazamiento de bytes.
tc_buffer_delete Borrar un rango de bytes.
tc_buffer_replace_utf16 Reemplazar un rango de unidades UTF-16 — la forma de una edición de AppKit.
tc_buffer_text Copiar el contenido completo.
tc_buffer_len_bytes / tc_buffer_len_utf16 Longitudes en ambas unidades.
tc_document_new / tc_document_open / tc_document_free Crear un documento (vacío o desde un archivo) y destruirlo.
tc_document_replace_utf16 Editar un documento registrando el historial de deshacer.
tc_document_undo / tc_document_redo Recorrer el historial; un parámetro de salida informa de la edición que la carcasa debe reproducir.
tc_document_break_undo_group Terminar la racha actual de fusión de deshacer.
tc_document_save / tc_document_save_as Guardados atómicos; los fallos rellenan un parámetro de salida opcional con un mensaje.
tc_document_text / tc_document_len_bytes / tc_document_len_utf16 Contenido y longitudes.
tc_document_is_dirty / tc_document_can_undo / tc_document_can_redo Consultas de estado.
tc_document_path / tc_document_encoding_name Identidad del archivo y codificación.
tc_string_free Liberar una cadena devuelta por el núcleo.

Las operaciones de archivo falibles siguen una convención más: devuelven su valor de fallo y, cuando quien llama pasó un parámetro de salida no nulo, almacenan allí un mensaje UTF-8 legible (que se libera con tc_string_free) — la carcasa lo muestra tal cual en la alerta.

La superficie es deliberadamente pequeña y crece solo cuando una característica de la carcasa lo necesita. Los datos masivos (futuro: tramos de coloreado, diagnósticos) cruzarán como estructuras compactas o cargas serializadas, no como una llamada por elemento.

Cómo lo consume Swift

El objetivo CTextchum envuelve la cabecera como módulo de Clang, de modo que Swift la importa como cualquier biblioteca. TextchumKit traduce después las llamadas crudas a Swift idiomático — clases con propiedad basada en deinit, parámetros NSRange, errores lanzados para operaciones rechazadas y eventos tipados entregados en el actor principal. El código de la aplicación nunca toca un puntero.