SGCG

…esto no es un subtítulo…

Ir a: contenido categorías calendario archivo suscripción

Volver arriba

Emacs Lisp en casos prácticos (6): gestor de temporizadores (2)

2015-07-20

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

Menú de temporizadores.
Menú de temporizadores.

En el artículo de hoy, continuación del de ayer, crearemos un modo mayor que nos servirá para gestionar temporizadores, algo que es útil si vamos a desarrollar algún código que los use.

Ayer creamos un modo mayor derivado de tabulated-list-mode, timer-menu-mode, que sirve para mostrar y gestionar temporizadores (unos elementos que sirven para ejecutar tareas tras cierto tiempo, opcionalmente de forma periódica). La definición del modo hace refencia a varias funciones que hoy definiremos.

Llenado de la tabla de temporizadores

Ayer hicimos que la tabla de temporizadores se actualizara con la función timer-menu--refresh que añadimos como función hook a tabulated-list-revert-hook. Lo único que tiene que hacer nuestra función timer-menu--refresh es actualizar el contenido de tabulated-list-entries:

(defun timer-menu--refresh () "Recompute the list of timers for the Timer Menu buffer." (setq tabulated-list-entries (timer-menu--entries)))

¡Así, llamando a otra función (timer-menu--entries) para generar las entradas, es fácil! ¿Qué hace esta función? Lo siguiente:

(defun timer-menu--entries () "Compute the list of entries for the tabulated list." (mapcar (lambda (timer) (list timer (vector "" (format "%S" (timer--repeat-delay timer)) (format "%S" (timer--function timer))))) timer-list))

Lo que generamos es lo que dice la documentación de tabulated-list-entries: una lista cuyos elementos son a su vez listas cuyo primer elemento es el identificador de la entrada y cuyo segundo elemento es un vector con el texto de cada celda que hay que mostrar. En nuestro caso, construimos la lista con mapcar sobre timer-list (que contiene los temporizadores vivos en este momento); para cada temporizador, construimos una entrada: el propio temporizador actúa como identificador y las celdas son un texto vacío (que se transformará en «D» en las entradas que marquemos para cancelar), el tiempo entre repeticiones (obtenido con timer--repeat-delay) y la función a llamar cuando el temporizador salta (obtenida con timer--function).

Cancelación de temporizadores

El código es similar al que creamos en el artículo en el que añadimos funciones de eliminación de subprocesos a process-menu-mode. Podemos marcar temporizadores para cancelar (con lo que aparecen con la marca «D»), desmarcarlos (con lo que la marca desaparece) y cancelar los temporizadores marcados. El código es casi idéntico al de aquel artículo y sería razonable sacar la funcionalidad común y meterla en una biblioteca. Sin más explicación, ahí queda:

(defun timer-menu-mark-for-deletion () "Mark the timer on this Timer List buffer line for deletion." (interactive) (tabulated-list-set-col 0 "D" t) (forward-line))

(defun timer-menu-unmark () "Unmark the timer on this Timer List buffer line." (interactive "P") (tabulated-list-set-col 0 "" t) (forward-line))

(defun timer-menu-execute () "Delete the timers marked for deletion." (interactive) (dolist (entry tabulated-list-entries) (let ((timer (elt entry 0)) (mark (elt (elt entry 1) 0))) (if (string-equal "D" mark) (cancel-timer timer)))) (revert-buffer))

Cambio del tiempo de espera entre repeticiones

Sería fácil hacer una función interactiva que pidiera los segundos de espera entre repeticiones y los aplicara al temporizador elegido:

(defun timer-menu-set-repeat-delay (delay) "Set the repeat DELAY seconds of the selected timer." (interactive "nSeconds: ") (let ((timer (tabulated-list-get-id))) (when timer (timer-set-time timer (timer--time timer) delay) (revert-buffer))))

Esta función pide de forma interactiva un número de segundos con interactive. Después, obtiene el temporizador seleccionado (con tabulated-list-get-id, que devuelve el identificador de la entrada seleccionado; construimos las entradas para que los identificadores fueran los temporizadores correspondientes) y, si este temporizador no es nulo (si hay realmente una entrada seleccionada), modifica el retardo con timer-set-time y actualiza la tabla de temporizadores con revert-buffer.

Sería conveniente hacer la función un poquito más inteligente: que pida los segundos entre repeticiones solamente si hay una entrada seleccionada y que ofrezca como valor por defecto el tiempo asignado actualmente. Podemos hacer esto con una declaración de función interactiva un poco más complicada que construya la lista de argumentos de forma explícita:

(defun timer-menu-set-repeat-delay (timer delay) "Set the repeat DELAY seconds of the selected TIMER." (interactive (let ((id (tabulated-list-get-id))) (list id (if id (read-number "Seconds: " (timer--repeat-delay id)) nil)))) (when timer (timer-set-time timer (timer--time timer) delay) (revert-buffer)))

Añadimos un argumento adicional, timer, para poder pasar el temporizador elegido al cuerpo de la función y así evitar llamar a tabulated-list-get-id por segunda vez.

Función interactiva para abrir el gestor de temporizadores

El convenio para los modos derivados de tabulated-list-mode es que haya una función interactiva cuyo nombre empiece por list- y que sirva para crear un nuevo búfer con el modo activado. Vamos a hacer lo propio con la siguiente función, list-timers:

(defun list-timers () "Display a list of all the timers managed by Emacs." (interactive) (let ((buffer (get-buffer-create "*Timer List*"))) (with-current-buffer buffer (timer-menu-mode) (list-timers--refresh) (tabulated-list-print)) (display-buffer buffer)))

Código

El código está disponible en el siguiente enlace: timer-menu-mode.el


Categorías: Informática

Permalink: http://sgcg.es/articulos/2015/07/20/emacs-lisp-en-casos-practicos-6-gestor-de-temporizadores-2/