Documentos
Un búfer es texto crudo; un documento es un búfer más todo lo que lo convierte en un archivo: un historial de deshacer, un indicador de cambios sin guardar, una ruta y una codificación. Las ventanas del editor trabajan siempre con documentos.
Archivos nuevos
⌘N abre un búfer sin título de texto plano; File → New with Format lista todos los lenguajes conocidos, así que un archivo nuevo puede colorearse antes de tener nombre (el lenguaje se mantiene hasta que el primer guardado nombre al archivo, que vuelve a detectar por la extensión como siempre). Guardar como para un documento sin título empieza en la carpeta del archivo que estaba al frente cuando se creó — un archivo nuevo suele pertenecer al proyecto en el que se estaba — y sugiere la extensión del lenguaje elegido.
Escritura
Retorno aplica sangría automática: la línea nueva hereda el espacio en
blanco inicial de la actual, y baja un nivel más cuando la línea termina
(antes del cursor) con un abridor — {, [, ( o :. El nivel extra
habla el dialecto del propio documento: tabuladores en un archivo
sangrado con tabuladores, espacios al ancho de tabulación configurado en
caso contrario. Una línea sin nada que heredar recibe un salto de línea
normal, así que la función es invisible hasta que ayuda.
Deshacer y rehacer
El historial de deshacer vive en el núcleo, no en el NSUndoManager de
AppKit. Cada edición se registra como una operación invertible; deshacer
extrae el registro más reciente, aplica su inversa e informa del cambio
resultante a la ventana, que lo reproduce en pantalla. Como el historial
está detrás de la misma interfaz que todo lo demás, no puede perderse
ninguna edición: no existe un segundo camino hacia el texto.
Los registros se fusionan para que deshacer avance en pasos a escala humana:
- Las rachas de tecleo se fusionan: inserciones consecutivas, cada una comenzando exactamente donde terminó la anterior, se convierten en un solo paso de deshacer.
- Las rachas de borrado se fusionan igual, tanto con retroceso como con suprimir.
- Un salto de línea termina la racha por cualquiera de sus lados, así que deshacer se detiene con granularidad de línea.
- Mover el cursor (clic, flechas) termina la racha actual; la siguiente tecla inicia un paso nuevo.
Las operaciones compuestas se registran como grupos explícitos: Reemplazar todo reescribe cada coincidencia pero se deshace como un solo paso, y una recarga desde disco (más abajo) también es un solo paso.
Un renombrado o una acción de código de un servidor de lenguaje es un solo paso en cada archivo que cambió. Cuando cambió más de uno, Deshacer en cualquiera de ellos lo retira entero: en cada documento abierto cuyo paso más nuevo siga siendo ese, y en los archivos que no estaban abiertos, que el editor escribió él mismo y de los que guardó una copia. Rehacer lo vuelve a hacer en todos. Un archivo cambiado desde entonces — a mano o por otro programa — se deja como está, y el área de notificaciones dice cuál, junto con cuántos archivos se deshicieron. Vale durante la sesión: tras reiniciar, el historial de cada documento empieza de nuevo.
Varios cursores
Añadir la siguiente aparición (⌃G, Alt+J en Linux) selecciona la
palabra del cursor; pulsado otra vez, añade la siguiente coincidencia de
lo seleccionado como una selección propia, dando la vuelta al final del
archivo — solo palabras enteras cuando la selección es una palabra
entera. Añadir cursor arriba y abajo (⌃⇧↑ y ⌃⇧↓, Alt+Mayús+Arriba
y Abajo en Linux) añaden un cursor en la línea siguiente hacia arriba o
hacia abajo, en la columna del primer cursor o al final de la línea si es
más corta. Los tres están en el menú Edit y se pueden reasignar como
addNextOccurrence, addCaretAbove y addCaretBelow.
Con varios cursores, escribir, pegar, Retroceso, Suprimir, Retorno y Tabulador ocurren en cada uno, como un solo paso de deshacer; las flechas, Inicio y Fin mueven todos los cursores, y con Mayús extienden todas las selecciones. Los cursores que se encuentran se vuelven uno. Escape, cuando no queda nada más que cerrar, deja solo el cursor más nuevo; también un clic, o cualquier otra orden, que trata de un solo cursor.
Todavía no: una columna de cursores se hace línea a línea, no arrastrando; Retorno y Tabulador con varios cursores insertan un salto de línea y un tabulador simples, sin la sangría que recibe un solo cursor; los movimientos por palabra y las demás órdenes dejan solo el cursor más nuevo; y en Linux el texto compuesto por un método de entrada va solo al cursor más nuevo.
Plegado
Fold (Ctrl+[) pliega el bloque que abre la línea del cursor; la línea que dice qué es el bloque se queda, y se esconde todo lo que sigue hasta el final. Fold All (Ctrl+Alt+[) lo hace con todos los bloques, de fuera adentro, y Unfold All (Ctrl+]) los devuelve. Los bloques salen del mismo árbol que usa el coloreado.
Un pliegue por línea de apertura, y el más ancho cuando varios bloques
empiezan ahí: impl Item { abre tanto el impl como su cuerpo, y plegar
el impl es lo que se quería. Un bloque que escondería una sola línea no
se ofrece: en un lenguaje de llaves esa línea es la de cierre, y en el
resto ahorra una línea a cambio de un adorno en todas las demás.
Plegar cambia lo que se ve y nada más. El texto no se toca, así que lo que se guarda es lo que había.
En macOS todavía no está. TextKit 2 dispone una línea por elemento de texto y no tiene ningún atributo que diga que se salte uno: un elemento devuelto vacío sigue ocupando una línea, y un elemento retirado de la enumeración lo dispone bien un layout manager pelado pero no se dibuja nada a través del controlador de viewport de la vista. Ambas cosas se midieron, no se supusieron.
Transformaciones
Edit ▸ Transform actúa sobre la selección, o sobre el documento entero cuando no hay nada seleccionado —que es de lo que trata la operación cuando no se ha señalado ninguna parte:
- Mayúsculas, minúsculas, tipo título y caso invertido.
- Ordenar líneas, ordenarlas al revés, quitar líneas duplicadas.
- Unir líneas, quitar los espacios finales.
- Convertir los finales de línea a LF o a CRLF.
Una operación sobre líneas recibe líneas enteras: una selección que empieza a mitad de línea crece hasta los límites que la rodean, porque ordenar media línea no lo ha pedido nadie. El texto que entró con CRLF sale con CRLF, salvo que la conversión sea justo el objetivo. El tramo transformado queda seleccionado, así que otra operación puede seguirla sin volver a seleccionarlo, y todo junto es un solo paso de deshacer.
El tipo título empieza cada palabra y baja el resto, con el apóstrofo
contando como parte de la palabra: don't be well-known queda como
Don't Be Well-Known. Quitar duplicados conserva el primero de cada
uno y no toca el orden. Unir descarta la sangría propia de cada línea,
que estaba ahí para colocarse bajo la línea de arriba.
Buscar y reemplazar
⌘F abre la barra de búsqueda nativa (⌥⌘F con el campo de reemplazo, ⌘G / ⇧⌘G coincidencia siguiente y anterior, ⌘E busca la selección). El menú de opciones de la barra ofrece coincidencia por subcadena, palabra completa y expresión regular. Los reemplazos son ediciones normales: pasan por el núcleo, entran en el historial de deshacer — Reemplazar todo como un solo paso — y marcan el documento como modificado igual que el tecleo.
Cambios externos
Textchum vigila el archivo de cada documento abierto. Si otro programa lo cambia:
- un documento limpio sigue al disco en silencio — la ventana muestra simplemente el contenido nuevo;
- un documento modificado pregunta: conservar los cambios sin guardar o recargar desde disco. Recargar descarta el búfer en favor del archivo, pero la recarga es en sí misma un paso de deshacer, así que ⌘Z devuelve su versión (y vuelve a marcar el documento como modificado, pues entonces difiere del disco).
Un archivo que desaparece del disco se deja en paz: el búfer permanece y guardar recrea el archivo.
Revertir a lo guardado (menú File, ⌥⌘R, reasignable como
revertToSaved) es la versión manual de la misma recarga: descartar el
búfer y aceptar lo que diga el disco, con una confirmación cuando hay
cambios sin guardar (y un Deshacer para recuperarlos). Existe para el
raro cambio externo que el vigilante no ve — flujos de borrar y
reemplazar como un git checkout.
Historial local
Cada guardado conserva la versión que escribió, aparte del archivo, y
el primer guardado de un archivo conserva también la versión que
reemplazó — así se puede recuperar un guardado que no debió ocurrir, un
formateador que se pasó o un trabajo descartado por un checkout.
File → Historial local… (reasignable como showLocalHistory) lista
las versiones conservadas del archivo en primer plano, de la más nueva a
la más vieja, cada una con las líneas que restaurarla añadiría y
quitaría; la seleccionada se muestra junto a la lista como la diferencia
con el texto actual. Elegir una la pone en el editor como una sola
edición: Deshacer la retira, y guardar la conserva, como una versión
nueva.
Las versiones viven en una carpeta history entre los datos del editor
(~/Library/Application Support/Textchum/history en macOS,
~/.local/share/textchum/history en Linux, o bajo --data-dir), una
carpeta por archivo. Una versión igual a la más nueva no se guarda dos
veces. Se conservan hasta 100 versiones por archivo, durante 30 días; un
archivo de más de 4 MB no se conserva.
Todavía no: solo se conservan los guardados, no el texto entre ellos; las versiones de un archivo borrado no se pueden abrir desde el editor, aunque siguen en la carpeta hasta cumplir sus 30 días; y cuánto tiempo se conservan no es un ajuste.
Estado de cambios sin guardar
Un documento conoce el punto exacto de su historial en el que se guardó por última vez, así que modificado significa «el estado actual difiere del guardado» — no «hubo una edición en algún momento». Editar y luego deshacer hasta el punto de guardado deja un documento limpio, y el botón de cerrar de la ventana pierde su punto en consecuencia. Si nuevas ediciones vuelven inalcanzable el estado guardado (se deshizo más allá de él y se escribió otra cosa), el documento cuenta como modificado hasta el próximo guardado, como debe ser.
Cerrar una ventana con cambios, o salir con ventanas modificadas abiertas, hace la pregunta habitual: guardar, no guardar o cancelar.
Archivos y codificaciones
Textchum descodifica al abrir y recodifica al guardar:
- El UTF-8 válido se carga como UTF-8. Un BOM inicial se elimina en memoria, se recuerda y se vuelve a escribir al guardar.
- Cualquier otra cosa se descodifica como ISO-8859-1 (Latin-1), que asigna un carácter a cada byte y por tanto no puede fallar. Al guardar se recodifica a Latin-1; si una edición introdujo caracteres que Latin-1 no puede contener, el guardado promociona el archivo a UTF-8 en silencio — nada puede perderse en esa dirección — y el subtítulo de la ventana refleja la nueva codificación.
Los finales de línea nunca se normalizan: lo que se leyó es lo que se
escribe, sea \n o \r\n.
La codificación actual está siempre visible en el subtítulo de la ventana, junto al tamaño del documento.
Los guardados son atómicos
Un guardado escribe el documento completo en un archivo temporal dentro del directorio de destino, lo vuelca a disco y lo renombra sobre el destino. Un fallo a mitad de guardado no puede dejar nunca un archivo truncado, y otros programas que observen el archivo ven el contenido viejo o el nuevo — nunca una mezcla.
Restauración de sesión
Relanzar Textchum reabre los archivos que estaban abiertos, cada uno con
su posición de cursor y de desplazamiento, trayendo al frente el que se
estaba usando. El estado es un archivo JSON plano (session.json, junto
a la configuración), escrito continuamente — no solo al salir —, así que
un fallo pierde como mucho un instante de posición, nunca la lista de
archivos. Los archivos que ya no existen se omiten.
Para arrancar sin memoria (útil persiguiendo un error): lanzar con
--fresh, mantener ⇧ mientras arranca la aplicación o borrar
session.json — cualquiera de las tres es un reinicio completo.
Cerrar una pestaña tampoco es definitivo: Reabrir pestaña cerrada (⇧⌘T en macOS, Ctrl+Shift+T en Linux) devuelve la última, con su cursor, y sigue hacia atrás por las recientes. Solo se recuerdan los documentos guardados: un búfer sin nombre no tiene de dónde volver, y reabrirlo vacío sería mentir.
Aún no está
- Codificaciones más allá de UTF-8 y Latin-1.