Variables de modo, keymaps y menús
Estamos acostumbrados a ajustar los paquetes que instalamos para que
se comporten de una u otra forma y eso es lo que vamos a hacer ahora
con nuestro modo de ejemplo datos-mode
. El código de ejemplo
utilizará dos variables para ello.
Imaginaos que estamos muy cansados de escribir el día de la fecha y el pie de firma en los documentos que escribo y quiero que lo haga Emacs por mí, porque soy así de vago y lo automatizo todo. Voy a crear una función que lo haga por mí. ¡Eso está «chupao»!¡Al lío! El pie de firma suele tener un formato más o menos fijo: Normalmente el lugar donde se firma, la fecha y a continuación quién lo firma.
(defun escribe-pie-firma () "Escribe el pie de firma en el lugar del punto." (interactive) (insert (concat "En Macondo, a " (format-time-string "%d de %B de %Y") "\nFdo: Aureliano Buendía")))
La función es sencilla y se explica sola: inserta una cadena que ha
formado concat
juntando varias cadenas, el lugar, el día y la
firma. Las he puesto en tres líneas para que se vean a simple vista y
no se nos despisten.
Variables
Pero ¿qué ocurre si quiero que mi modo lo utilice también otra persona? ¿Y si tengo que firmar estando en otro lugar? Pues nada más fácil que poner eso en variables, de manera que cambiando el valor de la variable, cambia el texto que se introducirá.
;; Definir variables globales (defvar datos-lugar "Macondo" "Lugar que se escribirá con la función de pie de firma.") (defvar datos-usuario "Aureliano Buendía" "Usuario que es escribirá con la función de pie de firma.")
Hemos definido dos variables, datos-lugar
y datos-usuario
, para
guardar aquellas cadenas que pueden variar. Se puede apreciar que las
he inicializado con dos valores. Lo habitual es que se dejaran vacías
o nil
y luego en el código controlar qué comportamiento es el
adecuado por defecto. Pero eso añadía complejidad a la función de
ejemplo con varias formas if
que podían confundir al novato
pareciendo que la función definitiva es más compleja de lo que en
realidad es:
(defun escribe-pie-firma () "Escribe el pie de firma en el lugar del punto." (interactive) (insert (concat "En " datos-lugar ", a " (format-time-string "%d de %B de %Y") "\nFdo: " datos-usuario)))
De este modo, la función es idéntica a la anterior, pero colocando las
variables en el lugar adecuado. Si la utilizamos directamente veremos
que el resultado es equivalente... ¿pero qué ocurre si en nuestro
init.el
colocamos el siguiente código después de la importación del
paquete?
(setq datos-lugar "Notxería") (setq datos-usuario "Notxor de la Notxería")
¡Ahí va! !Así es como funcionan las configuraciones de Emacs¡
Algunos pensarán: ¡Qué tontería, eso ya lo sabíamos! Por contra
otros acabarán de comprender que realmente init.el
no es más que un
fichero de elisp que funciona ajustando los distintos paquetes al
comportamiento que queremos de ellos.
Keymap
También habrá quien piense que al final escribir M-x escribe-dia
o
M-x escribe-pie-firma
no me ahorran mucho trabajo porque sigo
teniendo mucho que escribir. Quizá se estén preguntado o incluso estén
tentados a establecer combinaciones de teclas para las funciones. Eso
lo podemos hacer también desde el mismo modo, pero además me
facilitará que pueda explicar algunas cosas más adelante.
Definir keymap
:
;; Definir las combinaciones de teclas del modo (defvar datos-mode-map (let ((datos-mode-map (make-keymap))) (define-key datos-mode-map "\C-c\C-f" 'escribe-dia) (define-key datos-mode-map "\C-c\C-p" 'escribe-pie-firma) datos-mode-map) "Kaymap para datos-mode.")
Como se puede ver nuestro keymap
lo llamamos datos-mode-map
y lo
metemos en una variable con ese nombre. Se utiliza la forma let
para
crear con make-keymap
un keymap
local al que añadir las
combinaciones que necesito con la forma define-key
. Esta forma
necesita como primer argumento el keymap
en el que definirá la
combinación que se pasa en forma de cadena y por último la función que
debe llamar. Cuando se han añadido todas las combinaciones que
necesitamos, la forma let
evalúa el keymap
que se ha modificado
para que se establezca en la variable... creo que es más complicado de
explicar que de entender viendo el código.
Ahora hay que decirle al modo cuál es su keymap
, por tanto
modifico la definición para añadirlo. La definición quedará así:
;; Definición del modo menor (define-minor-mode datos-mode "Toggle Datos mode. Interactivamente sin argumento, este comando des/activa el modo." ;; El valor inicial. :init-value nil ;; Indicador de modo en la línea. :lighter " Datos" ;; Keymap para el modo :keymap datos-mode-map)
Como se puede apreciar, sólo he añadido :keymap datos-mode-map
. Y
ahora cuando recargo el mi modo ya puedo utilizar la combinación de
teclas que está definida (y que puedo cambiar en la configuración de
init.el
si quiero).
Menú
Hace tiempo que dejé de utilizar los menús con profusión, aunque de vez en cuando los miro y los uso, más con el objeto de memorizar la combinación de teclas que por comodidad. Pero hay gente que se pierde con estas cosas de la tecnología y necesita un buen menú en el que pinchar. Para ellos es más claro y siempre mi modo parecerá mucho más «pofesioná» (a ojos del público común) si tiene un menú.
Para definir el menú hay varios modos, pero el que recomiendo para
novatos como yo es el macro easy-menu-define
. Pongo el código y lo
explico:
(easy-menu-define datos-menu datos-mode-map "Menú para el modo datos-mode." '("Datos" ["Escribir día" escribe-dia] ["Escribir pie de firma" escribe-pie-firma]))
El macro define datos-menu
como el menú para el modo y utiliza para
ello datos-mode-map
como keymap
. Luego vemos que está el
comentario para documentar qué es y una lista. La primera cadena
"Datos"
será la que se muestre en el menú superior de Emacs y
luego están dos vectores para cada uno de los widgets de menú que
aparecerán. Se define la cadena que mostrará el menú y la función a la
que asociarlo.
Conclusión
Como vemos, con muy poco código tentemos un modo funcional y que da el pego, con chismáticos en los que pinchar quien sea más pusilánime y necesite llevar la flechita por toda la pantalla para asegurarse en su lentitud que lo está haciendo correctamente.
Pongo también el código completo tal y como ha quedado en el fichero
datos-mode.el
por si alguien se ha perdido desde las explicaciones
de la anterior entrega y ésta.
;;; Inicio de datos-mode (defvar datos-mode-hook nil) ;; Definir las combinaciones de teclas del modo (defvar datos-mode-map (let ((datos-mode-map (make-keymap))) (define-key datos-mode-map "\C-c\C-f" 'escribe-dia) (define-key datos-mode-map "\C-c\C-p" 'escribe-pie-firma) datos-mode-map) "Kaymap para datos-mode.") ;; Definir variables globales (defvar datos-lugar "Macondo" "Lugar que se escribirá con la función de pie de firma.") (defvar datos-usuario "Aureliano Buendía" "Usuario que es escribirá con la función de pie de firma.") (easy-menu-define datos-menu datos-mode-map "Menú para el modo datos-mode." '("Datos" ["Escribir día" escribe-dia] ["Escribir pie de firma" escribe-pie-firma])) ;; Definición del modo menor (define-minor-mode datos-mode "Toggle Datos mode. Interactivamente sin argumento, este comando des/activa el modo." ;; El valor inicial. :init-value nil ;; Indicador de modo en la línea. :lighter " Datos" ;; Keymap para el modo :keymap datos-mode-map) (defun escribe-dia (&opcional cadena-formato) "Escribe el día de la fecha en lugar del punto." (interactive "P\nsCadena de formato: ") (if (> (length cadena-formato) 0) (insert (format-time-string cadena-formato)) (insert (format-time-string "%Y-%m-%d")))) (defun escribe-pie-firma () "Escribe el pie de firma en el lugar del punto." (interactive) (insert (concat "En " datos-lugar ", a " (format-time-string "%d de %B de %Y") "\nFdo: " datos-usuario))) ;; Informa que este fichero proporciona el modo datos-mode (provide 'datos-mode) ;;; Fin de datos-mode
Como se puede ver, contamos ya con una plantilla para hacer un modo muy sencilla que podemos abarcar aún de un vistazo.