Oh! Emacs, el sistema operativo que resulta que tiene un editor incorporado y al que solo le falta el fregadero de la cocina, aunque incomprendido por muchos, alabado por otros y quizás hasta odiados por alguna minoría es, desde luego, una pieza de software que no te dejará indiferente.
¿Qué es Emacs? Pues un intérprete de EmacsLisp.
A diferencia de otros editores, Emacs no utiliza un lenguaje de extensión junto a una API que puedes usar para escribir extensiones, es más correcto pensar en Emacs como un intérprete sobre el cual se ha construido un framework para editar texto pero nada le impide ampliar este framework para soportar otras tareas como interactuar con una base de datos o con un terminal o con un servidor de mensajería.
Analizando con sloccount el código fuente de Emacs:
Totals grouped by language (dominant language first):
lisp: 1068797 (77.75%)
ansic: 280312 (20.39%)
objc: 15002 (1.09%)
sh: 6263 (0.46%)
perl: 1277 (0.09%)
pascal: 1010 (0.07%)
cs: 770 (0.06%)
cpp: 528 (0.04%)
awk: 477 (0.03%)
ruby: 257 (0.02%)
java: 27 (0.00%)vemos que cerca del 20% del código está escrito en C (unas 280 mil líneas), esto es básicamente la implementación de un intérprete de EmacsLisp, el casi 78% restante es código EmacsLisp (alrededor de 1 millón de líneas) que no es más que el framework que le da el carácter de editor de texto entre otras cosas a Emacs.
De hecho, generar el ejecutable de Emacs requiere dos pasos:
- Compilar el código C que genera un ejecutable llamado
temacsy que es puramente un intérprete de EmacsLisp + rutinas de I/O. - El ejecutable
temacsse ejecuta para cargar la librería estándard escrita en EmacsLisp y volcar todo a un ejecutable con esas librerías pre-cargadas por razones de eficiencia.
Al último ejecutable es a lo que conocemos como Emacs.
¿Qué diferencia significa el hecho que sea un intérprete?
Entre otras cosas, que absolutamente todo lo que haces en el programa, en última instancia, invoca a una función escrita en EmacsLisp (la mayoría) o en C (por razones de eficiencia).
Por ejemplo, el atajo de teclado <Alt> - m posiciona el cursor en el
primer caracter de la línea que sea un espacio o un tabulador, pero si
buscamos la ayuda de este comando nos dice lo siguiente:
M-m runs the command back-to-indentation, which is an interactive compiled Lisp function in `simple.el'. It is bound to M-m. (back-to-indentation) Move point to the first non-whitespace character on this line.
- La funcionalidad tiene un nombre: back-to-indentation
- Está definida en un sitio concreto: simple.el que es un enlace al
archivo del mismo nombre que no es más que código EmacsLisp, al que
puedes acceder interactivamente y ver su implementación:
(defun back-to-indentation () "Move point to the first non-whitespace character on this line." (interactive "^") (beginning-of-line 1) (skip-syntax-forward " " (line-end-position)) ;; Move back over chars that have whitespace syntax but have the p flag. (backward-prefix-chars))
- La documentación te dice a qué atajo está asociada la función. Es más, si cambias el atajo, el cambio será reflejado en la documentación.
- Como la funcionalidad no es más que una función de EmacsLisp puedes crear tu propia versión de la función y que Emacs utilice esa para lo cual ni tienes que reiniciar el programa ni compilar nada, solo evaluar (recuerda que es un intérprete) la función que defines y listo.
Este es el gran conocimiento que Emacs tiene sobre sí mismo y el hecho de que puedas programar o re-programar toda su funcionalidad (incluyendo las funciones escritas en C) para tu propia conveniencia es lo que más me fascina, después de todo soy un programador no? :)
La misma funcionalidad en VIM por ejemplo está mapeada
a al atajo ^ en modo normal, si buscamos la ayuda del comando
(:help ^) obtenemos lo siguiente:
^
^ To the first non-blank character of the line.
exclusive motion.
- Para VIM
^es simplemente^. No tiene un nombre como en el caso de Emacs. - No puedes acceder a su implementación de forma interactiva, para
acceder a ella tienes que bucear en el código.
src/normal.c:// ... {'^', nv_beginline, 0, BL_WHITE | BL_FIX}, // ... /* * "0" and "^" commands. * cap->arg is the argument for beginline(). */ static void nv_beginline(cap) cmdarg_T *cap; { cap->oap->motion_type = MCHAR; cap->oap->inclusive = FALSE; beginline(cap->arg); #ifdef FEAT_FOLDING if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) foldOpenCursor(); #endif ins_at_eol = FALSE; /* Don't move cursor past eol (only necessary in a one-character line). */ }
- La ayuda no es más que una sección de un fichero de texto estático
que alguien escribió. Si haces un
remapde la funcionalidad y vuelves a consultar la ayuda verás que te sigue diciendo lo mismo. - La funcionalidad de
^no puede ser re-escrita utilizando vimscript.
Es decir, VIM no es un “sistema vivo” en la forma en que Emacs lo es.
Disclaimer: No intento iniciar otra guerra santa, he utilizado como ejemplo VIM porque es lo que utilizaba antes y era muy feliz, me encantaba! Hoy en día utilizo Emacs porque se adapta mejor a mis necesidades: aprender Lisp, funcionalidades de IDE, etc.
Citando a Wikipedia:
Emacs adapta su comportamiento al tipo de texto que está editando mediante modos de edición llamados “modos mayores” (“major modes”). Los modos mayores se definen para textos de texto ordinario, código fuente para diversos lenguajes de programación, documentos HTML, TeX y LaTeX y muchos otros tipos de texto. Cada modo mayor modifica ciertas variables en Lisp para que Emacs se comporte de forma más conveniente para ese tipo concreto de texto. Los modos mayores también ofrecen comandos especiales de edición. Por ejemplo, los modos mayores para lenguajes de programación definen habitualmente comandos para saltar al principio o al final de una función.
El comportamiento de Emacs puede ser más personalizado aún utilizando los “modos menores” (“minor modes”). Mientras que sólo se puede asociar un modo mayor con un buffer a la vez, se puede tener activos varios modos menores. Por ejemplo, el modo mayor para el lenguaje de programación C define un modo menor diferente para cada uno de los estilos de indentación más populares.
Es decir, un modo mayor viene siendo lo que el filetype es para VIM y los modos menores son como si fueran plugins que puedes auto-activar para ciertos filetypes o de forma globals.
El modeline o línea de modos es la línea que aparece abajo del todo y que se le llama de modos porque entre otras cosa te muestra los modos que están activos.
Es lo que contiene el texto que estás editando. Cada vez que abres un fichero se crea un buffer con el mismo nombre que ese fichero y que contiene el texto del mismo.
- El atajo
C-x C-fque invoca al comandofind-filese utiliza para cargar un fichero en un buffer. - El atajo
C-x bque invoca al comandoswitch-to-bufferse utiliza para moverte entre varios buffers. - El atajo
C-x C-sque invoca al comandosave-bufferse utiliza para volcar el contenido del buffer a un fichero. - El comando
rename-bufferse utiliza para cambiar el nombre del buffer.
Los buffers pueden o no estar asociados a ficheros.
Por convención los buffers que no están asociados a ficheros se
escriben entre *, por ejemplo, el buffer *scratch* es un buffer
que no está asociado a ningún fichero.
El nombre de un buffer y el fichero al que está asociado son cosas independientes,
puedes renombrar un buffer que está asociado a un fichero y guardar C-x C-s y seguirá
guardándose en el mismo fichero.
Un window en Emacs es la región que encierra un buffer y que tiene un modeline. La siguiente imagen muestra 4 ventanas:
Un frame no es más que una ventana GUI.
Meta es probablemente lo mismo que tu tecla Alt y se abrevia M en
los comandos, por ejemplo: el comando M-x significa <Alt> - x.
Es tu tecla Ctrl y se abrevia C en los comandos, por ejemplo: el
comando C-a significa <Ctrl> - a.
Es tu tecla espacio y se abrevia SPC en los comandos, por ejemplo: el
comando C-SPC significa <Ctrl> - <Space>.
Un comando es una función del editor que se puede invocar a través de
un prompt que aparece al presionar M-x.
Es fácil distinguir estas funciones si estás mirando código EmacsLisp
porque tienen la lista (interactive) en su definición.
Por ejemplo, la función back-to-indentation es un comando.
Su definición original es (fíjate en la línea con (interactive)):
(defun back-to-indentation ()
"Move point to the first non-whitespace character on this line."
(interactive "^")
(beginning-of-line 1)
(skip-syntax-forward " " (line-end-position))
;; Move back over chars that have whitespace syntax but have the p flag.
(backward-prefix-chars))Y por el hecho de ser un comando podemos invocarla directamente a través del prompt sin necesidad de un atajo, es más, al final eres tú el que decide qué comandos quieres tener mapeados a qué atajos de teclado (solo los comandos se pueden mapear a atajos):
- Presiona
M-x - Escribe
back-toy da al tabulador para que autocomplete - Intro
El resultado es el mismo que invocar el comando con un atajo.
Probemos con otro ejemplo, hemos dicho que Emacs es un intérprete de
EmacsLisp no? Pues eso significa que tendríamos que poder ejecutar el
código EmacsLisp que quisiéramos no? Pues bien, el comando
eval-expression que por defecto está mapeado a M-: nos permite
precisamente esto:
- Presiona
M-x - Escribe
eval-expression - Intro
- Escribe:
(message "Estamos en el año: %s" (format-time-string "%Y"))
El resultado deberá aparecer debajo de la línea de modos.
El punto o point no es más que la posición del cursor dentro del buffer.
Emacs mantiene una estructura de datos que se llama mark-ring que es una lista circular. La marca o mark es una posición del buffer que insertas al principio del mark-ring (la lista circular).
C-SPCque invoca al comandoset-mark-commandinserta la posición actual del cursor y si mueves el cursor sobresalta el texto.C-SPC C-SPCque invoca al comandoset-mark-commandinserta la posición actual del cursor sin sobresaltar el texto.C-u C-SPCque invoca al mismo comandoset-mark-commandpero mueve el cursor a la posición guardada al principio del mark-ring y mueve esa posición al final del mark-ring (rota la lista).
Es decir, que puedes ir dejando rastros con C-SPC por todo el buffer
y luego volver sobre tus pasos con C-u C-SPC volviendo a empezar
cuando llegues al final del mark-ring. El mark-ring por defecto tiene
un máximo de tamaño de 16 marcas.
A la región del buffer entre el punto y la marca se le llama region y muchos comandos actúan sobre ella. Por ejemplo:
comment-regionque comenta la region.kill-regionmapeado aC-wpor defecto que corta el texto en la region.kill-ring-savemapeado aM-wpor defecto que copia el texto en la region.
Emacs mantiene otra estructura de datos que se llama kill-ring que es otra lista circular.