El infierno del indentado
Estos días he estado trabajando en un tema en Python. En otros lenguajes, el indentado del código es más una cuestión estética que funcional, pero ya sabéis que ese lenguaje utiliza, precisamente el indentado, para establecer los bloques de código. Es una forma elegante de obligar al programador a escribir código legible, quiera o no, pero viendo lo que suele ocurrir con ello, se convierte en el origen de muchas frustraciones. ¿Por qué? Pues básicamente porque a cada programador le gusta una forma de hacerlo y eso implica, también, que los editores de texto lo hagan por defecto a la manera que le guste a quien lo programó, que puede coincidir o no con el gusto del usuario, y/o con el gusto del programador del lenguaje, y/o con el de los que hicieran el estándar del mismo. Esto, básicamente, es lo que yo llamo el infierno del indentado.
En emacs el indentado sigue siendo un infierno, además, no define ninguna manera por defecto y se lo deja al usuario y al programador, haciendo habitual que sea cada modo mayor el encargado de velar por la correcta estructura del código que se esté trabajando. Esto tiene sus ventajas y sus desventajas. Entre las ventajas, claro está, la capacidad de que teniendo buffers abiertos con distintos lenguajes, cada uno de ellos utilice el indentado que corresponda sin interferir en los otros buffers. Y la principal entre las desventajas es que la mayoría de programadores vienen acostumbrados de otros editores a esperar un comportamiento uniforme en ese sentido y a tener que cambiarlo cada vez que cambias de lenguaje... y a cambiarlo a su gusto, naturalmente; encontrándose que después de haber gastado tiempo en configurar los espacios y tabuladores, resulta que abren un fichero de código y directamente pasan de sus configuraciones.
Hace tiempo que dejé de pegarme con el indentado en emacs, simplemente espero que funcione correctamente, porque me tiene acostumbrado a ello: pulsas enter cuando acabas una línea y el cursor va a la posición que corresponda según los usos y costumbres del lenguaje en el que estás programando y no me preocupo de más. Hasta aquí todo correcto y si eres el único programador del proyecto en el que estés y lo haces siempre con el mismo editor y no hay interferencias de otros editores, todo funciona perfectamente. Pero en un mundo ideal, si todo funcionara según las intenciones del que actúa y no según sus acciones, nos aburriríamos demasiado.
Descubriendo incongruencias en el indentado
Como decía, había estado trabajando un poco con Python y siendo ese lenguaje tan especial con el tema de los espacios al principio de línea, me encontré con el infierno del indentado de nuevo.
Por poner un ejemplo y que se entienda, vamos a analizar el siguiente código:
def con_argumento( arg="" ): if arg: print("El argumento es: ", arg) else: print("No hay argumento") con_argumento("Hola") con_argumento()
Mientras esperas que la salida sea algo como:
$ python3 prueba.py El argumento es: Hola No hay argumento
Resulta que lo que lanza es algo como:
$ python3 prueba.py File "prueba.py", line 3 print("El argumento es: ", arg) ^ TabError: inconsistent use of tabs and spaces in indentation
¡Mierda! ¡Ya estamos! ... A ver; vamos por partes y os lo explico todo ─al menos lo poco que yo sé─.
Preferencias personales
Cuando uno tiene que lidiar con este tema, termina haciéndose una idea
de cómo le gusta o no le gusta a él el indentado. En emacs, como
en otros editores se pueden establecer esos gustos por defecto en el
fichero init.el
. Por poner mi ejemplo, yo tengo establecidos los
siguientes parámetros en la lista de custom-set-variables
:
'(indent-tabs-mode nil) '(tab-width 4)
También se podría establecer fuera de dicha lista con:
(setq indent-tabs-mode nil) (setq tab-width 4)
La primera variable estable que no se utilicen tabuladores para el
indentado y que sean reemplazados por espacios y la segunda
variable, establece que cada tabulador tenga un ancho de 4
. El
evitar que utilice tabuladores evita que al pasar de un editor a otro,
o cuando ves el código en github, por ejemplo, todo el indentado
se haya ido al garete.
Como ya he dicho, luego cada modo mayor establece sus parámetros al gusto del programador del modo, que suele coincidir con lo más habitual de cada lenguaje. Pero es interesante establecer los valores por defecto, para los casos en que los modos no tratan el indentado en su código.
¿Y qué ocurre si mis gustos personales no coinciden con los que tenga quien programó el modo de emacs? Pues tienes dos opciones, te acostumbras a lo que diga el modo o te creas un gancho, del estilo:
(add-hook 'python-mode-hook (function (lambda () (setq indent-tabs-mode nil) (setq tab-width 4))))
Ese código se ejecutará después de establecerse el modo Python, por ejemplo, y ajustará los valores deseados a como tú los quieras. Pero es muy cansino hacerlo para todos y cada uno de los modos que puedes utilizar.
Visualizar los espacios en emacs
Todo esto, está muy bien: ya tenemos controlada ─un poco─ la fiera del
indentado... ¿Qué pasa cuando nos llega código de otros, escrito con
otros editores o con otros ajustes o gustos? Primero de todo es
identificar dónde está el problema visualizándolo de manera directa.
Si estás acostumbrado a programar en Python y a utilizar editores
diseñados para la programación en dicho lenguaje, estarás más que
harto de ver cómo el editor te muestra los saltos de tabulación y los
espacios para delimitar los bloques de código. Eso también se puede
visualizar de forma sencilla en emacs con el modo menor
whitespace-mode
.
En este caso, no hay que instalar nada, pues es uno de los modos que
se incluyen en la base de emacs, sólo hay que activarlo llamando a
whitespace-mode
(o desactivarlo, con el mismo comando). Al activarse
mostrará ws
en la línea de estado.
Si lo activas y no lo has utilizado nunca, ni lo has configurado de
otra manera, verás los espacios marcados por un carácter ·
, los
saltos de línea con $
, los tabuladores con »
. Todo eso se puede
configurar, no sólo los colores que utilizará, sino también los
caracteres por otros más a tu gusto o costumbre... En mi caso en su
día utilicé el carácter |
para el tabulador y el ←
para el final
de línea, pero quité los ajustes en una remodelación de mi init.el
y
ya me he acostumbrado a los valores por defecto.
Todo esto al final también es una cuestión de gustos, o más que gustos, de costumbre... pero que sepáis que poder se puede.
Cambiar el indentado sin morir en el intento
Hemos activado el modo y descubierto que nuestro código luce más o menos así:
def·con_argumento(·arg=""·):$ ····if·arg:$ » ····print("El·argumento·es:·",·arg)$ ····else:$ ········print("No·hay·argumento")····$ ····$ con_argumento("Hola")$ con_argumento()$
Además lo marca con colores y vemos que tenemos algunas líneas que
tienen al final espacios en blanco que sobran y que la tercera línea
está mezclando caracteres »
y ·
, lo cual indica que los primeros
cuatro espacios son un tabulador y los siguientes cuatro son espacios
en blanco. ¿Cómo se quita? Muy fácil, con un sencillo par de pasos:
- Marcamos las líneas que queremos modificar.
- Llamamos al comando
untabify
, ─si queremos utilizar espacios en blanco─, o al comandotabify
, ─si queremos utilizar tabuladores─, ya sabéis: va en gustos.
Cuando utilizamos tabify
(o untabify
) es posible que no veamos que
emacs hace algo, porque el alineamiento puede estar mostrando por
pantalla las cosas de manera normal: como hemos visto en el ejemplo,
si el tabulador está fijado a 4
y el código está escrito para
ajustarse a esos cuatro espacios, visualmente no se apreciará el
cambio, a no ser que tengamos activado el modo whitespace
. Algo más
habitual de lo que se cree, porque los errores cuando no coinciden el
ancho de tabuladores y los espacios, saltan a la vista; pero cuando
coinciden y no se ven a simple vista, suele ser cuando más molestos de
encontrar son.
Y ya está explicado todo lo que sé sobre el infierno del indentado y cómo enfrentarse a él desde emacs.
Conclusión
Es algo que hace que me rechinen los dientes cuando tropiezo en
ello. Cada vez que me encuentro con un problema de estos me da vueltas
el higadillo y la bilis lo salpica todo. Tengo que respirar hondo un
par de veces y luego ya, activo whitespace-mode
para visualizar los
errores. Como ya habéis visto, es fácil corregirlos e inviertes sólo
un rato, pero un rato que deberías estar dedicando a otra cosa
importante: como comer, dormir, etc.
También quiero destacaros que no os dejéis llevar por las prisas y
marquéis todo el buffer con C-x h
y llaméis al comando
(un)tabify
para cambiar todo de golpe. A ver, poder se puede, pero
esos comandos no distinguen si los espacios están o no al principio de
las líneas y puedes estar modificando también espacios o tabuladores
puestos para hacer una tabla en los comentarios u otros formateos
de texto, como alinear las entradas de variables para visualizarlas
mejor, etc. Dicho de otro modo: «Usadlo con cuidado».